diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 128a57c..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: deploy -on: {push: {branches: [source]}} -defaults: - run: - working-directory: repo -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: { path: repo } - - uses: actions/setup-node@v2 - with: - node-version: '14' - - uses: Bogdanp/setup-racket@v1.5 - with: - version: '8.1' - dest: '$GITHUB_WORKSPACE/racket' - sudo: never - - - name: install - run: | - yarn install - raco pkg install --batch --installation --auto --update-deps --link blog - - name: build - run: | - yarn run build - racket blog/build.rkt - - - name: deploy - run: | - cd output - git init -b gh-pages - git config user.name 'GitHub Actions' - git config user.email 'lexi.lambda@gmail.com' - git add . - git commit -m 'Deploy to GitHub Pages' - git push --force 'https://lexi-lambda:${{ github.token }}@github.com/${{ github.repository }}' gh-pages diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 19d74c2..0000000 --- a/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -compiled/ -doc/ -*~ -/node_modules -/bower_components - -/build -/output diff --git a/about.html b/about.html new file mode 100644 index 0000000..74b2102 --- /dev/null +++ b/about.html @@ -0,0 +1,8 @@ +About me

About me

My name is Alexis King, and I write a lot of software. I currently live in Chicago.

I’m interested in functional programming, static types, and programming language research, and I try to spend as much time as I can writing Haskell and Racket. I write about some of the things I do on this blog, and I sometimes tweet about them and other things on Twitter. I work on a lot of open-source projects on GitHub, and you can email me at lexi.lambda@gmail.com.

Things I’ve worked on

This list is just a smattering of the cooler things I’ve worked on over the past few years. It is neither exhaustive nor particularly scientific in its curation.

2021 +Reimplemented this blog on top of Scribble, replacing frog. Released megaparsack v1.5, adding support for user-defined parser state and parser lookahead.

2020 +Published “Macros for DSLs” with Michael Ballantyne and Matthias Felleisen at OOPSLA. Presented “Effects for Less” at ZuriHac 2020 on the design of eff and its accompanying GHC proposal to add delimited continuations to the RTS. Led a large-scale refactoring effort to statically rule out several classes of bugs in graphql-engine. Opened a GHC proposal for improving arrow notation. Started contributing to GHC, including some performance fixes and improvements to arrow notation.

2019 +Published “Does blame shifting work?” with Lukas Lazarek, Samanvitha Sundar, Robby Findler, and Christos Dimoulas at POPL. Released monad-validate based on work done for Hasura. Started working at Hasura.

2018 +Presented “Hackett: a metaprogrammable Haskell” at Curry On, and again a few months later at Strange Loop. Started working on contract systems with Christos Dimoulas at Northwestern University.

2017 +Released the freer-simple package. Presented “Hackett, a Haskell for Racketeers” at RacketCon. Started working on Hackett, a Haskell-like language embedded in Racket.

2016 +Started learning about type systems to explore ideas inspired by Turnstile. Presented “Languages in an Afternoon” at RacketCon. Released the megaparsack and scripty Racket libraries. Started writing Haskell professionally for the first time.

About this blog

This blog is powered by Scribble, an unusually flexible document preparation system written in Racket. Unlike most markup languages, every Scribble document is a program that evaluates to a document, which can then be rendered using one of a number of different backends. Scribble provides a TeX-like notation for writing such programs, but unlike TeX—which is essentially an overgrown macro preprocessor—Scribble is a full-fledged functional programming language. In fact, the Scribble syntax is really just an alternate notation for Racket itself, so all the libraries and abstractions available in Racket can be used more or less directly in a Scribble document.

This makes Scribble a remarkably powerful tool for writing prose documents, and indeed, it serves as the foundation for Racket’s best-in-class documentation system, among other things. Using it to power a blog is perhaps a bit overkill, but it gives me the wonderful ability to define whatever abstractions I desire to make blogging as effortless as possible. For example, after finding myself linking to Hackage packages quite frequently, I decided to define a one-line function:

(define (hackage-package package-name)
+  (hyperlink (string-append "https://hackage.haskell.org/package/" package-name) package-name))

Now all I have to do is write @hackage-package{lens} and I get lens. Sure, it’s not exactly mind-blowing, but it’s certainly convenient… and of course, the most significant advantages involve abstractions too elaborate to describe here.

This site is, naturally, open source, so if you’d like to see how all the pieces fit together for yourself, feel free to clone the GitHub repository. And if you’re interested in a simple example of what Scribble looks like to use, you might as well take a peek at the source code for this page in particular.

\ No newline at end of file diff --git a/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/index.html b/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/index.html new file mode 100644 index 0000000..a21d753 --- /dev/null +++ b/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/index.html @@ -0,0 +1,79 @@ +Automatically deploying a Frog-powered blog to GitHub pages

Automatically deploying a Frog-powered blog to GitHub pages

⦿ racket, frog, meta

So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses Greg Hendershott's fantastic Frog tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via Travis CI, so my blog is always up-to-date.

Setting up Frog

I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple raco pkg install frog followed by raco frog --init and raco frog -bp created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is raco frog -n "Post Title", and you're good to go.

By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use Sass for my stylesheets, potentially with support for CoffeeScript later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used Gulp in conjunction with NPM for build and dependency management.

Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.

Configuring automatic deployment with Travis

Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being this Gist, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special gh-pages branch.

To make this easy, Frog can be configured to output to a separate directory via the .frogrc configuration file. I chose to output to the out directory:

output-dir = out
+

I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.

$ cd out
+$ git init
+$ git add .
+$ git commit -m "Deploy to GitHub Pages"
+$ git push --force "$REMOTE_URL" master:gh-pages
+

The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's encryption keys along with a GitHub personal access token. Just install the Travis CLI client, copy the access token, and run a command:

$ gem install travis
+$ travis encrypt GH_TOKEN=<access token...>
+

The output of that command is an encrypted value to be placed in an environment variable in the project's .travis.yml configuration file. The URL for the repository on GitHub will also need to be specified as well:

env:
+  global:
+  - GH_REF: 'github.com/<gh-username>/<gh-repo>.git'
+  - secure: <encrypted data...>

Now all that's left is configuring the .travis.yml to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to python, then installed Racket and Frog as pre-installation steps.

env:
+  global:
+  - GH_REF: 'github.com/<gh-username>/<gh-repo>.git'
+  - secure: <encrypted data...>
+  - RACKET_DIR: '~/racket'
+  - RACKET_VERSION: '6.2'
+
+before_install:
+- git clone https://github.com/greghendershott/travis-racket.git
+- cat travis-racket/install-racket.sh | bash
+- export PATH="${RACKET_DIR}/bin:${PATH}"
+
+install:
+- raco pkg install --deps search-auto frog

(It might be worth noting that Greg Hendershott also maintains the repository that contains the above Travis build script!)

Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses master, not gh-pages. Obviously, I didn't want Travis running on my master branch, since it would be deploying to that, so I added a branch whitelist:

branches:
+  only:
+  - source

All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:

#!/bin/bash
+set -ev # exit with nonzero exit code if anything fails
+
+# clear the output directory
+rm -rf out || exit 0;
+
+# build the blog files + install pygments for highlighting support
+npm install
+npm run build
+pip install pygments
+raco frog --build
+
+# go to the out directory and create a *new* Git repo
+cd out
+git init
+
+# inside this git repo we'll pretend to be a new user
+git config user.name "Travis CI"
+git config user.email "<your@email.here>"
+
+# The first and only commit to this new Git repo contains all the
+# files present with the commit message "Deploy to GitHub Pages".
+git add .
+git commit -m "Deploy to GitHub Pages"
+
+# Force push from the current repo's master branch to the remote
+# repo. (All previous history on the branch will be lost, since we are
+# overwriting it.) We redirect any output to /dev/null to hide any sensitive
+# credential data that might otherwise be exposed.
+git push --force --quiet "https://${GH_TOKEN}@${GH_REF}" master > /dev/null 2>&1

For reference, my final .travis.yml looked like this:

language: python
+python:
+- '3.4'
+
+branches:
+  only:
+  - source
+
+env:
+  global:
+  - GH_REF: 'github.com/lexi-lambda/lexi-lambda.github.io.git'
+  - secure: <long secure token...>
+  - RACKET_DIR: '~/racket'
+  - RACKET_VERSION: '6.2'
+
+before_install:
+- git clone https://github.com/greghendershott/travis-racket.git
+- cat travis-racket/install-racket.sh | bash
+- export PATH="${RACKET_DIR}/bin:${PATH}"
+
+install:
+- raco pkg install --deps search-auto frog
+
+script: bash ./deploy.sh

That's it! Now I have a working blog that I can publish just by pushing to the source branch on GitHub.

    \ No newline at end of file diff --git a/blog/2015/08/22/deploying-racket-applications-on-heroku/index.html b/blog/2015/08/22/deploying-racket-applications-on-heroku/index.html new file mode 100644 index 0000000..fff843d --- /dev/null +++ b/blog/2015/08/22/deploying-racket-applications-on-heroku/index.html @@ -0,0 +1,24 @@ +Deploying Racket applications on Heroku

    Deploying Racket applications on Heroku

    ⦿ racket, heroku, 12factor

    Heroku is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.

    Building the server

    Racket's web-server package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:

    #lang racket
    +
    +(require web-server/servlet
    +         web-server/servlet-env)
    +
    +(define (start req)
    +  (response/xexpr
    +   '(html (head (title "Racket Heroku App"))
    +          (body (h1 "It works!")))))
    +
    +(serve/servlet start #:servlet-path "/")

    Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the PORT environment variable. We can access this using the Racket getenv[racket] function.

    Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass #f for the #:listen-ip argument.

    (define port (if (getenv "PORT")
    +                 (string->number (getenv "PORT"))
    +                 8080))
    +(serve/servlet start
    +               #:servlet-path "/"
    +               #:listen-ip #f
    +               #:port port)

    Also, by default, serve/servlet[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.

    (serve/servlet start
    +               #:servlet-path "/"
    +               #:listen-ip #f
    +               #:port port
    +               #:command-line? #t)

    That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.

    Setting up our app for Heroku

    The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:

    $ git init
    +$ heroku git:remote -a racket-heroku-sample
    +$ heroku buildpacks:set https://github.com/lexi-lambda/heroku-buildpack-racket

    We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the RACKET_VERSION environment variable as follows:

    $ heroku config:set RACKET_VERSION=6.2.1

    Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.

    Specifically, we just want to run our serve.rkt module. The Racket buildpack installs the repository as a package, so we can run racket with the -l flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:

    web: racket -l sample-heroku-app/server
    +

    Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can navigate to our app's URL and actually see it running live!

    Conclusion

    That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.

    That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can find it on GitHub here. For more details on the buildpack itself, it's also available on GitHub here.

      \ No newline at end of file diff --git a/blog/2015/08/30/managing-application-configuration-with-envy/index.html b/blog/2015/08/30/managing-application-configuration-with-envy/index.html new file mode 100644 index 0000000..377b1da --- /dev/null +++ b/blog/2015/08/30/managing-application-configuration-with-envy/index.html @@ -0,0 +1,18 @@ +Managing application configuration with Envy

      Managing application configuration with Envy

      ⦿ envy, racket, 12factor

      Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, The Twelve-Factor App provides a set of standards for keeping web apps sane, and one of those guidelines advises keeping configuration in the environment.

      Envy is the declarative bridge between Racket code and the outside world of the environment.

      Introducing Envy

      I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require envy and you're good to go.

      The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:

      ; environment.rkt
      +#lang typed/racket/base
      +
      +(require envy)
      +
      +(define/provide-environment
      +  api-token
      +  [log-level : Symbol #:default 'info]
      +  [parallel? : Boolean])

      When this module is required, Envy will automatically do the following:

      1. Envy will check the values of three environment variables: API_TOKEN, LOG_LEVEL, and PARALLEL.

      2. If either API_TOKEN or PARALLEL is not set, an error will be raised:

        envy: The required environment variable "API_TOKEN" is not defined.
        +
      3. The values for LOG_LEVEL and PARALLEL will be parsed to match their type annotations.

      4. If LOG_LEVEL is not set, it will use the default value, 'info.

      5. The values will be stored in api-token, log-level, and parallel?, all of which will be provided by the enclosing module.

      Now just (require (prefix-in env: "environment.rkt")), and the environment variables are guaranteed to be available in your application's code.

      Working with Typed Racket

      As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, define/provide-environment will only work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.

      However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.

      > parallel?
      +- : Boolean
      +#t
      +

      Envy really shines when using optional environment variables with the #:default option. The type of the value given to #:default doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.

      > (define-environment
      +    [num-threads : Positive-Integer #:default #f])
      +> num-threads
      +- : (U Positive-Integer #f)
      +#f
      +

      This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.

      And more...

      To see the full set of features that Envy already provides, take a look at the documentation. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, open an issue and make a suggestion! I already have plans for a #lang envy DSL, which will hopefully cut the boilerplate out in its entirety.

      And finally, to give credit where credit is due, Envy is heavily inspired by Envied (both in name and function), an environment variable manager for Ruby, which I've used to great effect.

      Try it out!

        \ No newline at end of file diff --git a/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/index.html b/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/index.html new file mode 100644 index 0000000..27f92e2 --- /dev/null +++ b/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/index.html @@ -0,0 +1,13 @@ +Canonical factories for testing with factory_girl_api

        Canonical factories for testing with factory_girl_api

        ⦿ ruby, rails, javascript, angular

        Modern web applications are often built as single-page apps, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.

        To attempt to address a fraction of this problem, I built factory_girl_api, a way to share context setup between both sides of the application.

        A brief overview of factory_girl

        In the land of Ruby and Rails, factory_girl is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:

        FactoryGirl.define do
        +  factory :widget do
        +    sequence(:name) { |id| 'Widget #' + id }
        +    price 10
        +
        +    trait :expensive do
        +      price 1000
        +    end
        +  end
        +end

        This makes it easy to create new instances of Widget and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:

        widget = FactoryGirl.create :widget

        We can also create more expensive widgets by using the :expensive trait.

        expensive_widget = FactoryGirl.create :widget, :expensive

        Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.

        fancy_widget = FactoryGirl.create :widget, :expensive, name: 'Fancy Widget'

        It works well, and it keeps initialization boilerplate out of individual tests.

        Testing on the front-end

        Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:

        var fancyWidget = new Widget({
        +  name: 'Fancy Widget',
        +  price: 1000
        +});

        Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a single, canonical source for all of our factories.

        Reusing server-side factories with factory_girl_api

        To help alleviate this problem, I created the factory_girl_api gem for Rails and the angular-factory-girl-api Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.

        The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:

        FactoryGirl.create('widget', 'expensive', { name: 'Fancy Widget' });

        In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.

        The problems with relying on the server for data

        In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not completely convinced it's the right solution yet.

        First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.

        My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.

        Potential improvements and other paths to success

        I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This kind of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.

        Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!

          \ No newline at end of file diff --git a/blog/2015/11/06/functionally-updating-record-types-in-elm/index.html b/blog/2015/11/06/functionally-updating-record-types-in-elm/index.html new file mode 100644 index 0000000..c533710 --- /dev/null +++ b/blog/2015/11/06/functionally-updating-record-types-in-elm/index.html @@ -0,0 +1,66 @@ +Functionally updating record types in Elm

          Functionally updating record types in Elm

          ⦿ elm

          Elm is a wonderful language for building web apps, and I love so much of its approach to language design. Elm does so many things right straight out of the box, and that's a real breath of fresh air in the intersection of functional programming and web development. Still, it gets one thing wrong, and unfortunately, that one thing is incredibly important. Elm took the "functions" out of "functional record types".

          Almost any software program, at its core, is all about data. Maybe it's about computing data, maybe it's about manipulating data, or maybe it's about displaying data, but at the end of the day, some sort of data model is going to be needed. The functional model is a breathtakingly elegant system for handling data and shuttling it around throughout a program, and functional reactive programming, which Elm uses to model event-like interactions, makes this model work even better. The really important thing, though, is what tools Elm actually gives you to model your data.

          A brief primer on Elm records

          Elm supports all the core datatypes one would expect—numbers, strings, booleans, optionals, etc.—and it allows users to define their own types with ADTs. However, Elm also provides another datatype, which it calls "records". Records are similar to objects in JavaScript: they're effectively key-value mappings. They're cool data structures, and they work well. Here's an example of creating a Point datatype in Elm:

          type alias Point =
          +  { x : Float, y : Float }

          Notice that Point is declared as a type alias, not as a separate type like an ADT. This is because record types are truly encoded in the type system as values with named fields, not as disparate types. This allows for some fun tricks, but that's outside the scope of this blog post.

          The good

          What I'd like to discuss is what it looks like to manipulate these data structures. Constructing them is completely painless, and reading from them is super simple. This is where the record system gets everything very right.

          origin : Point
          +origin = { x = 0, y = 0 }
          +
          +distanceBetween : Point -> Point -> Float
          +distanceBetween a b =
          +  let dx = a.x - b.x
          +      dy = a.y - b.y
          +  in sqrt (dx*dx + dy*dy)

          The syntax is clean and simple. Most importantly, however, the record system is functional (in the "functional programming" sense). In a functional system, it's useful to express concepts in terms of function composition, and this is very easy to do in Elm. Creating a function to access a field would normally be clunky if you always needed to do record.field to access the value. Fortunately, Elm provides some sugar:

          -- These two expressions are equivalent:
          +(\record -> record.field)
          +.field

          Using the .field shorthand allows writing some other functions in terms of composition, as most functional programmers would desire:

          doubledX : Point -> Float
          +doubledX = ((*) 2) << .x

          This satisfies me.

          The bad

          So if everything in Elm is so great, what am I complaining about? Well, while the syntax to access fields is convenient, the syntax to functionally set fields is questionably clunky. Consider a function that accepts a point and returns a new point with its x field set to 0:

          zeroedX : Point -> Point
          +zeroedX point = { point | x <- 0 }

          This doesn't look too bad, does it? It's clear and concise. To me, though, there's something deeply wrong here... this function has a lot of redundancy! It seems to me like we should be able to write this function more clearly in a point-free style. The .field shorthand "functionalizes" the record getter syntax, so there must be a function version of the update syntax, right? Maybe it would look something like this:

          zeroedX : Point -> Point
          +zeroedX = !x 0

          But alas, there is no such syntax.

          Now you may ask... why does it matter? This seems trivial, and in fact, the explicit updater syntax may actually be more readable by virtue of how explicit it is. You'd be right, because so far, these examples have been horribly contrived. But let's consider a slightly more useful example: functionally updating a record.

          What's the difference? Well, say I wanted to take a point and increment its x field by one. Well, I can easily write a function for that:

          incrementX : Point -> Point
          +incrementX point = { point | x <- point.x + 1 }

          Not terrible, though a little verbose. Still, what if we want to also add a function that decrements x?

          decrementX : Point -> Point
          +decrementX point = { point | x <- point.x - 1 }

          Oh, gosh. That's basically the exact same definition but with the operation flipped. Plus we probably want these operations for y, too. Fortunately, there's an easy solution: just pass a function in to transform the value! We can define an updateX function that allows us to do that easily, then we can define our derived operations in terms of that:

          updateX : (Float -> Float) -> Point -> Point
          +updateX f point = { point | x <- f point.x }
          +
          +incrementX : Point -> Point
          +incrementX = updateX ((+) 1)
          +
          +decrementX : Point -> Point
          +decrementX = updateX (\x -> x - 1)

          Not only is that much cleaner, but we can now use it to implement all sorts of other operations that allow us to add, subtract, multiply, or divide the x field. Now we just need to generalize our solution to work with the x and y fields!

          Oh, wait. We can't.

          The ugly

          This is where everything breaks down completely. Elm does not offer enough abstraction to reduce this level of crazy duplication:

          updateX : (Float -> Float) -> Point -> Point
          +updateX f point = { point | x <- f point.x }
          +
          +incrementX : Point -> Point
          +incrementX = updateX ((+) 1)
          +
          +decrementX : Point -> Point
          +decrementX = updateX (\x -> x - 1)
          +
          +updateY : (Float -> Float) -> Point -> Point
          +updateY f point = { point | y <- f point.y }
          +
          +incrementY : Point -> Point
          +incrementY = updateY ((+) 1)
          +
          +decrementY : Point -> Point
          +decrementY = updateY (\x -> x - 1)

          We sure can give it a shot, though. At the very least, we can implement the increment and decrement functions in a more general way by passing in an updater function:

          increment : ((Float -> Float) -> a -> a) -> a -> a
          +increment update = update ((+) 1)

          Now, with updateX and updateY, we can increment either field very clearly and expressively. If we shorten the names to uX and uY, then the resulting code is actually very readable:

          pointAbove = uY (\x -> x + 1)
          +pointBelow = uY (\x -> x - 1)

          It's almost like English now: "update Y using this transformation". This is actually pretty satisfactory. The trouble arises when you have a struct with many fields:

          type alias PlayerStats =
          +  { health : Integer
          +  , strength : Integer
          +  , charisma : Integer
          +  , intellect : Integer
          +  -- etc.
          +  }

          It might be very convenient to have generic functional updaters in this case. One could imagine a game that has Potion items:

          type Potion = Potion String (PlayerStats -> PlayerStats)

          And then some different kinds of potions:

          potions =
          +  [ (Potion "Health Potion" (uHealth ((+) 1))),
          +  , (Potion "Greater Intellect Potion" (uIntellect ((+) 3)))
          +  , (Potion "Potion of Weakness" (uStrength (\x -> x // 5)))
          +  ]

          This is a really elegant way to think about items that can affect a player's stats! Unfortunately, it also means you have to define updater functions for every single field in the record. This can get tedious rather quickly:

          uHealth : (Integer -> Integer) -> PlayerStats -> PlayerStats
          +uHealth f stats = { stats | health <- f stats.health }
          +
          +uStrength : (Integer -> Integer) -> PlayerStats -> PlayerStats
          +uStrength f stats = { stats | strength <- f stats.strength }
          +
          +uCharisma : (Integer -> Integer) -> PlayerStats -> PlayerStats
          +uCharisma f stats = { stats | charisma <- f stats.charisma }
          +
          +-- etc.

          This is pretty icky. Could there be a better way?

          Trying to create a more general abstraction

          Interestingly, this pattern doesn't need to be this bad. There are better ways to do this. Let's revisit our updater functions.

          Really, update can be defined in terms of two other primitive operations: a read and a (functional) write. What would it look like if we implemented it that way instead of requiring special updater functions to be defined? Well, it would look like this:

          update : (a -> b) -> (b -> a -> a) -> (b -> b) -> a -> a
          +update get set f x = set (f (get x)) x

          The type definition is a little long, but it's really pretty simple. We just supply a getter and a setter, then a function to do the transformation, and finally a record to actually transform. Of course, as you can see from the type, this function isn't actually specific to records: it can be used with any value for which a getter and setter can be provided.

          The trouble here is that writing field setters isn't any easier in Elm than writing field updaters. They still look pretty verbose:

          sHealth : Integer -> PlayerStats -> PlayerStats
          +sHealth x stats = { stats | health <- x }
          +
          +uHealth : (Integer -> Integer) -> PlayerStats -> PlayerStats
          +uHealth = update .health sHealth

          So, at the end of it all, this isn't really a better abstraction. Still remember my fantasy !field setter shorthand half a blog post ago? Now perhaps it makes a little more sense. If such a syntax existed, then defining the updater would be incredibly simple:

          uHealth : (Integer -> Integer) -> PlayerStats -> PlayerStats
          +uHealth = update .health !health

          Still, no syntax, no easy updaters, and by extension, no easy, declarative description of behavior without quite a bit of boilerplate.

          Conclusions and related work

          Elm is a very promising language, and it seems to be in fairly rapid development. So far, its author, Evan Czaplicki, has taken a very cautious approach to implementing language features, especially potentially redundant ones. This caution is why things like operator slicing, "where" clauses, and special updater syntax have not yet made it into the language. Maybe at some point these will be deemed important enough to include, but for the time being, they've been excluded.

          I obviously think that having this sort of thing is incredibly important to being able to write expressive code without a huge amount of overhead. However, I also do not want to give the impression that I think adding special setter syntax is the only way to do it.

          Seasoned functional programmers will surely have noticed that many of these concepts sound a lot like lenses, and Elm actually already has a lens-like library authored by Evan himself, called Focus. This, however, does not actually solve the problem: it requires manual description of setters just like the purely function based approach does. Really, lenses are just the logical next step in the line of abstraction I've already laid out above.

          Interestingly, PureScript and Elm, the two Haskell-likes-on-the-frontend that I've paid attention to (though PureScript is much closer to Haskell than Elm), both have this very same problem. Haskell itself solves it with macros via Template Haskell. My favorite language, Racket, solves it with its own macro system. Is there another way to do these things that doesn't involve introducing a heavyweight macro system? Definitely. But I think this is a necessary feature, not a "nice to have", so if a macro system is out of the picture, then a simpler, less flexible solution is the obvious logical alternative.

          I really like Elm, and most of my experiences with it have been more than enough to convince me that it is a fantastic language for the job. Unfortunately, the issue of functional record updaters has been quite the frustrating obstacle in my otherwise frictionless ride. I will continue to happily use Elm over other, far less accommodating tools, but I hope that issues like these will be smoothed out as the language and its ecosystem matures.

            \ No newline at end of file diff --git a/blog/2015/12/21/adts-in-typed-racket-with-macros/index.html b/blog/2015/12/21/adts-in-typed-racket-with-macros/index.html new file mode 100644 index 0000000..4116a69 --- /dev/null +++ b/blog/2015/12/21/adts-in-typed-racket-with-macros/index.html @@ -0,0 +1,107 @@ +ADTs in Typed Racket with macros

            ADTs in Typed Racket with macros

            ⦿ racket, typed racket, macros

            Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate why macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is ADTs.

            Warning: this is not a macro tutorial

            First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you are interested in learning about macros, I must recommend Greg Hendershott's Fear of Macros. It is good. This is not that.

            Now, with that out of the way, let's get started.

            What we’re building

            Algebraic data types, or ADTs, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.

            Racket also already has a facility for creating custom data structures in the form of structs, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.

            With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:

            data Tree = Empty
            +          | Leaf Int
            +          | Node Tree Tree

            This already demonstrates a few of the core things we'll need to build:

            1. Each ADT has a data type, in this case Tree. This name only exists in the world of types, it isn't a value.

            2. Each ADT has various data constructors, in this case Leaf and Node.

            3. Each data constructor may accept any number of arguments, each of which have a specific type.

            4. The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.

            Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:

            data Tree a = Empty
            +            | Leaf a
            +            | Node (Tree a) (Tree a)

            With this in mind, we can add a fifth and final point to our list:

            1. ADTs must be able to be parametrically polymorphic.

            That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.

            Describing ADTs in Racket

            How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:

            (define-datatype (Tree a)
            +  Empty
            +  (Leaf a)
            +  (Node (Tree a) (Tree a)))

            This looks pretty good. Just like with the Haskell implementation, Tree should only exist at the type level, and Empty, Leaf, and Node should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be (Leaf 7).

            Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's match[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:

            (: tree-sum ((Tree Integer) -> Integer))
            +(define (tree-sum tree)
            +  (match tree
            +    [(Empty)    0               ]
            +    [(Leaf n)   n               ]
            +    [(Node l r) (+ (tree-sum l)
            +                   (tree-sum r))]))

            Given that Racket's struct form automatically produces identifiers that cooperate with match, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.

            Implementing ADTs as syntax

            Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, syntax/parse. The syntax/parse library works like the traditional Scheme syntax-case on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.

            Since this is not a macro tutorial, the following implementation assumes you already know how to use syntax/parse. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.

            Parsing types with a syntax class

            To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like Tree, or they can be identifiers with parameters, like (Tree a). We'll want to cover both cases.

            (begin-for-syntax
            +  (define-syntax-class type
            +    (pattern name:id #:attr [param 1] '())
            +    (pattern (name:id param ...+))))

            This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (...+) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use #:attr to give the param attribute the default value of an empty list, so this syntax class will actually normalize the input we get in addition to actually parsing it.

            A first attempt at define-datatype

            Now we can move on to actually implementing define-datatype. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using syntax-parser, which actually does the parsing for our macro.

            (define-syntax define-datatype
            +  (syntax-parser
            +    [(_ type-name:type data-constructor:type ...)
            +     ]))

            This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with _ (which will just be the identifier define-datatype), then expecting a type-name, which uses the type syntax class we defined above. Next, we expect zero or more data-constructors, which also use the type syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.

            Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:

            (define-syntax define-datatype
            +  (syntax-parser
            +    [(_ type-name:type data-constructor:type ...)
            +     #'(begin
            +         (struct data-constructor.name ([f : data-constructor.param] ...)
            +         ...))]))

            This is actually really close to being correct. This will generate a struct definition for each data-constructor, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have names, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for all the fields, f, so if any data constructor has two or more fields, we'll get an error.

            Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called generate-temporary, which generates random identifiers. Our next attempt might look like this:

            #`(begin
            +    (struct data-constructor.name
            +      ([#,(generate-temporary) : data-constructor.param] ...)
            +    ...))

            The #, lets us "escape" from the template to execute (generate-temporary) and interpolate its result into the syntax. Unfortunately, this doesn't work. We do generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.

            More leveraging syntax classes

            As it turns out, this is also easy to do with syntax classes. We can add an extra attribute to our type syntax class to generate a random identifier with each one. Again, we can use #:attr to do that automatically. Our new definition for type will look like this:

            (begin-for-syntax
            +  (define-syntax-class type
            +    (pattern name:id
            +             #:attr [param 1] '()
            +             #:attr [field-id 1] '())
            +    (pattern (name:id param ...+)
            +             #:attr [field-id 1] (generate-temporaries #'(param ...)))))

            Here we're using generate-temporaries instead of generate-temporary, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each param.

            We can now fix our macro to use this field-id attribute instead of the static field name:

            #'(begin
            +    (struct data-constructor.name
            +      ([data-constructor.field-id : data-constructor.param] ...))
            +    ...)

            Creating the supertype

            We're almost done—now we just need to implement our overall type, the one defined by type-name. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:

            (define-type Tree (U Empty Leaf Node))

            However, a polymorphic type alias would need to include the type parameters in each subtype, like this:

            (define-type (Tree a) (U (Empty a) (Leaf a) (Node a)))

            How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using procedural macros.

            To build each properly-instantiated type, we'll use a combination of define/with-syntax and Racket's list comprehensions, for/list. The define/with-syntax form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by syntax-parser. This will allow us to break up our result into multiple steps. Technically, define/with-syntax is not strictly necessary—we could just use #` and #,—but it's cleaner to work with.

            We'll start by defining a set of instantiated data constructor types, one per data-constructor:

            (define/with-syntax [data-type ...]
            +  (for/list ([name (in-syntax #'(data-constructor.name ...))])
            +    ))

            Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:

            (define/with-syntax [data-type ...]
            +  (for/list ([name (in-syntax #'(data-constructor.name ...))])
            +    (if (stx-null? #'(type-name.param ...))
            +        name
            +        #`(#,name type-name.param ...))))

            Now with our definition for data-type, we can implement our type alias for the supertype extremely easily:

            #'(define-type type-name (U data-type ...))

            Putting it all together

            There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by type-name are in scope for each data constructor's structure definition. We can do this by making use of type-name.param within each produced struct definition, resulting in this:

            #'(begin
            +    (struct data-constructor.name (type-name.param ...)
            +      ([data-constructor.field-id : data-constructor.param] ...))
            +    ...)

            And we're done! The final macro, now completed, looks like this:

            (begin-for-syntax
            +  (define-syntax-class type
            +    (pattern name:id
            +             #:attr [param 1] '()
            +             #:attr [field-id 1] '())
            +    (pattern (name:id param ...+)
            +             #:attr [field-id 1] (generate-temporaries #'(param ...)))))
            +
            +(define-syntax define-datatype
            +  (syntax-parser
            +    [(_ type-name:type data-constructor:type ...)
            +
            +     (define/with-syntax [data-type ...]
            +       (for/list ([name (in-syntax #'(data-constructor.name ...))])
            +         (if (stx-null? #'(type-name.param ...))
            +             name
            +             #`(#,name type-name.param ...))))
            +
            +     #'(begin
            +         (struct (type-name.param ...) data-constructor.name
            +           ([data-constructor.field-id : data-constructor.param] ...)) ...
            +         (define-type type-name (U data-type ...)))]))

            It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.

            Using our ADTs

            With the macro built, we can now actually use our ADTs using the syntax we described! The following is now valid code:

            (define-datatype (Tree a)
            +  Empty
            +  (Leaf a)
            +  (Node (Tree a) (Tree a)))
            +
            +> (Node (Leaf 3) (Node (Empty) (Leaf 7)))
            +- : (Node Positive-Byte)
            +(Node (Leaf 3) (Node (Empty) (Leaf 7)))

            We can use this to define common data types, such as Haskell's Maybe:

            (define-datatype (Maybe a)
            +  (Just a)
            +  Nothing)
            +
            +(: maybe-default (All [a] (Maybe a) a -> a))
            +(define (maybe-default m v)
            +  (match m
            +    [(Just a)  a]
            +    [(Nothing) v]))
            +
            +(: maybe-then (All [a] (Maybe a) (a -> (Maybe a)) -> (Maybe a)))
            +(define (maybe-then m f)
            +  (match m
            +    [(Just a)  (f a)]
            +    [(Nothing) (Nothing)]))

            And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:

            (define-datatype Expr
            +  (Value Number)
            +  (Add Expr Expr)
            +  (Subtract Expr Expr)
            +  (Multiply Expr Expr)
            +  (Divide Expr Expr))
            +
            +(: evaluate (Expr -> Number))
            +(define (evaluate e)
            +  (match e
            +    [(Value x)      x                            ]
            +    [(Add a b)      (+ (evaluate a) (evaluate b))]
            +    [(Subtract a b) (- (evaluate a) (evaluate b))]
            +    [(Multiply a b) (* (evaluate a) (evaluate b))]
            +    [(Divide a b)   (/ (evaluate a) (evaluate b))]))
            +
            +> (evaluate (Add (Value 1)
            +                 (Multiply (Divide (Value 1) (Value 2))
            +                           (Value 7))))
            +4 1/2

            There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, I've put together a gist here.

            Conclusions and credit

            This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly are powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.

            This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.

            That said, I think it's pretty cool.

            Finally, I must give credit where credit is due. Thanks to Andrew M. Kent for the creation of the datatype package, which served as the inspiration for this blog post. Many thanks to Sam Tobin-Hochstadt for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to Ryan Culpepper and Matthias Felleisen for their work on creating syntax/parse, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to Matthew Flatt for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.

            Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.

              \ No newline at end of file diff --git a/blog/2016/02/18/simple-safe-multimethods-in-racket/index.html b/blog/2016/02/18/simple-safe-multimethods-in-racket/index.html new file mode 100644 index 0000000..46499c1 --- /dev/null +++ b/blog/2016/02/18/simple-safe-multimethods-in-racket/index.html @@ -0,0 +1,105 @@ +Simple, safe multimethods in Racket

              Simple, safe multimethods in Racket

              ⦿ racket, macros

              Racket ships with racket/generic, a system for defining generic methods, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports single dispatch. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.

              Motivating multiple dispatch

              What is multiple dispatch and why is it necessary? Well, in most cases, it isn’t necessary at all. It has been shown that multiple dispatch is much rarer than single dispatch in practice. However, when actually needed, having multiple dispatch in the toolbox is a valuable asset.

              A classic example of multiple dispatch is multiplication over both scalars and vectors. Ideally, all of the following operations should work:

              2 × 3 = 6
              +2 × ⟨3, 4⟩ = ⟨6, 8⟩
              +⟨3, 4⟩ × 2 = ⟨6, 8⟩
              +

              In practice, most languages do not support such flexible dispatch rules without fairly complicated branching constructs to handle each permutation of input types. Furthermore, since most languages only support single dispatch (such as most object-oriented languages), it is nearly impossible to add support for a new combination of types to an existing method.

              To illustrate the above, even if a language supported operator overloading and it included a Vector class that overloaded multiplication to properly work with numbers and vectors, it might not implement matrix multiplication. If a user defines a Matrix class, they may overload its multiplication to support numbers, vectors, and matrices, but it is impossible to extend the multiplication implementation for the Vector class. That method is now completely set in stone, unless it is edited directly (and the programmer may not have access to Vector’s implementation).

              Multiple dispatch solves all of these problems. Rather than specify implementations of functions for singular types, it is possible to specify implementations for sets of types. In the above example, a programmer would be able to define a new function that operates on Vector and Matrix arguments. Since each definition does not “belong” to any given type, extending this set of operations is trivial.

              Multiple dispatch in Racket

              This blog post is somewhat long and technical, so before proceeding any further, I want to show some real code that actually works so you can get a feel for what I’m talking about. As a proof-of-concept, I have created a very simple implementation of multiple dispatch in Racket. The above example would look like this in Racket using my module:

              #lang racket
              +
              +(require multimethod)
              +
              +(provide mul
              +         (struct-out num)
              +         (struct-out vec))
              +
              +(struct num (val))
              +(struct vec (vals))
              +
              +(define-generic (mul a b))
              +
              +(define-instance ((mul num num) x y)
              +  (num (* (num-val x) (num-val y))))
              +
              +(define-instance ((mul num vec) n v)
              +  (vec (map (curry * (num-val n)) (vec-vals v))))
              +
              +(define-instance ((mul vec num) v n)
              +  (mul n v))

              Pardon the somewhat clunky syntax, but the functionality is there. Using the above code works as expected:

              > (mul (num 2) (num 3))
              +(num 6)
              +> (mul (num 2) (vec '(3 4)))
              +(vec '(6 8))
              +> (mul (vec '(3 4)) (num 2))
              +(vec '(6 8))
              +

              Making the above snippet work is not particularly hard. In fact, it’s likely that most competent Racketeers could do it without much thought. However, there’s a tiny bit more going on behind the scenes than it may seem.

              The problem with multiple dispatch

              The single-dispatch design limitation of racket/generic comes directly from a desire to avoid what has been described as “spooky action at a distance”, a problem that is prevalent in many systems that support methods with multiple dispatch (aka multimethods). Specifically, the issue arises when new method implementations are defined for existing datatypes, which can have far-reaching effects throughout a program because the method table is global state. Both CLOS and Clojure suffer from this shortcoming.

              Interestingly, Haskell with multi-parameter typeclasses (a nonstandard but highly useful extension) makes it quite trivial to create constructs similar to multiple dispatch (though the overload resolution is done at compile-time). The similarities are significant: Haskell also suffers from the possibility of a certain sort of “spooky action”. However, Haskell’s static typing and resolution allows the compiler to catch these potential issues, known as “orphan instances”, at compile time. Even though Racket does not support the same sort of static typing, the same idea can be used to keep multiple dispatch safe using the macro system.

              Safe, dynamically-typed multiple dispatch

              In order to make multiple dispatch safe, we first need to determine exactly what is unsafe. Haskell has rules for determining what constitutes an “orphan instance”, and these rules are equally applicable for determining dangerous multimethod implementations. Specifically, a definition can be considered unsafe if both of the following conditions are true:

              1. The multimethod that is being implemented was declared in a different module from the implementation.

              2. All of the types used for dispatch in the multimethod instance were declared in a different module from the implementation.

              Conversely, a multimethod implementation is safe if either of the following conditions are true:

              1. The multimethod that is being implemented is declared in the same module as the implementation.

              2. Any of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.

              Why do these two rules provide a strong enough guarantee to eliminate the dangers created by global state? Well, to understand that, we need to understand what can go wrong if these rules are ignored.

              Multimethods and dangerous instances

              What exactly is this dangerous-sounding “spooky action”, and what causes it? Well, the trouble stems from the side-effectful nature of multimethod instance definitions. Consider the Racket module from earlier, which defines multiplication instances for scalars and vectors:

              (provide mul
              +         (struct-out num)
              +         (struct-out vec))
              +
              +(struct num (val))
              +(struct vec (vals))
              +
              +(define-generic (mul a b))
              +
              +(define-instance ((mul num num) x y)
              +  (num (* (num-val x) (num-val y))))
              +
              +(define-instance ((mul num vec) n v)
              +  (vec (map (curry * (num-val n)) (vec-vals v))))
              +
              +(define-instance ((mul vec num) v n)
              +  (mul n v))

              Note that there is not actually a (mul vec vec) implementation. This is intentional: there are two ways to take the product of two vectors, so no default implementation is provided. However, it is possible that another module might desire an instance for mul that takes the dot product, and the programmer might write the following definition:

              (define-instance ((mul vec vec) x y)
              +  (num (foldl + 0 (map * (vec-vals x) (vec-vals y)))))

              However, there is something fishy about the above definition: it doesn’t need to be exported with provide to work! Since instances don’t create new bindings, they only add dispatch options, they don’t ever need to provide anything. This is problematic, though: it means that a program could continue to happily compile even if the module containing the dot product instance was never loaded with require, but an attempt to multiply two vectors would fail at runtime, claiming that there was no (mul vec vec) implementation. This drastic change of behavior violates Racket programmers’ assumptions about the guarantees made by modules (require should not cause any side-effects if the module’s bindings are not used).

              Of course, while this seems potentially unexpected, it is workable: just be careful to require modules containing instances. Unfortunately, it gets much worse—what if a different library defines its own (mul vec vec) instance? What if that instance takes the cross product instead? That library may function entirely properly on its own, but when loaded alongside the program that defines a dot product instance, it is impossible to determine which instance should be used where. Because define-instance operates by modifying the aforementioned global state, the implementations clash, and the two systems cannot continue to operate together as written.

              This is pretty bad. Defining extra instances is a reasonable use-case for multiple dispatch, but if these instances can break third-party code, how can they be trusted? This sort of problem can make multiple dispatch difficult to reason about and even more difficult to trust.

              What determines safety?

              With those problems in mind, we can turn back to the two rules for safe multiple dispatch. How do they prevent the above issues? Well, let’s take them one at a time.

              Remember that an instance can be unequivocally determined to be safe if either of the two conditions are true, so we can consider them entirely independently. The first one is simple—an instance is safe if the following condition holds:

              The multimethod that is being implemented is declared in the same module as the implementation.

              This one is pretty obvious. It is impossible to create a “bad” instance of a method declared in the same module because it is impossible to import the method without also bringing in the instance. Furthermore, a conflicting instance cannot be defined at the place where the types themselves are defined because that would require a circular module dependency, which Racket does not permit.

              With the above explanation in mind, the second condition should make sense, too:

              Any of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.

              The same argument for the first point holds for the second, but with the parties swapped. Again, it is impossible to use the instance without somehow requiring the module that defines the datatype itself, so the instance would always be required, anyway. The most interesting aspect of this condition is that it demonstrates that instances can be defined for existing datatypes (that are defined in other modules) just so long as at least one of the datatypes is defined in the same module. This continues to permit the important use-case of extending the interfaces of existing types.

              Encoding the safety rules into Racket’s macro system

              In order to keep track of which methods and instances are defined where, I leveraged a technique based on the one used by Typed Racket to keep track of whether or not a typed identifier is used in a typed or untyped context. However, instead of using a simple mutable boolean flag, I used a mutable free identifier set, which keeps track of the identifiers within a given module that should be considered “privileged”.

              #lang racket/base
              +
              +(require syntax/id-set)
              +
              +(provide mark-id-as-privileged!
              +         id-privileged?)
              +
              +(define privileged-ids (mutable-free-id-set))
              +
              +(define (mark-id-as-privileged! id)
              +  (free-id-set-add! privileged-ids id))
              +
              +(define (id-privileged? id)
              +  (free-id-set-member? privileged-ids id))

              Making this work with define-generic is obvious: just invoke mark-id-as-privileged! on the method name to note that the method is “privileged” in the scope of the current module. Keeping track of privileged structs is similarly straightforward, though it is a little more devious: the multimethod module provides a custom struct macro that just expands to struct from racket/base, but adds privilege information.

              The define-instance macro does all the heavy lifting to ensure that only privileged identifiers can be used in instance definitions. A simple check for the identifier annotations is performed before proceeding with macro expansion:

              (unless (or privileged? (ormap id-privileged? types))
              +  (assert-privileged-struct! (first types)))

              When the privilege checks fail, an error is raised:

              (define (assert-privileged-struct! id)
              +  (unless (id-privileged? id)
              +    (raise-syntax-error 'define-instance
              +                        "expected name of struct defined in current module"
              +                        id)))

              With the above safeguards in place, the dangerous dot product implementation from above would not be allowed. The checks manage to encode both of the safety rules into the macro system such that invalid instances will fail at compile time, preventing dangerous uses of multimethods from ever slipping by unnoticed.

              Actually implementing multiple dispatch

              The rest of the multimethod implementation is relatively straightforward and is not even particularly robust. If anything, it is the bare minimum of what would be needed to allow the safety mechanisms above to work. Lots of features that would likely be needed in a real implementation are not included, and graceful error handling is largely ignored.

              Multimethods themselves are implemented as Racket transformer bindings containing custom data, including a reference to the multimethod’s arity and dispatch table. The custom datatype includes a prop:procedure structure type property, which allows such bindings to also function as macros. The macro procedure expands to an operation that looks up the proper instance to use in the multimethod’s dispatch table and invokes it with the supplied arguments.

              The relevant code for defining multimethods is reproduced below:

              (begin-for-syntax
              +  (struct multimethod (arity dispatch-table)
              +    #:property prop:procedure
              +    (λ (method stx)
              +      (syntax-parse stx
              +        [(method arg ...)
              +         #'(apply-multimethod method (list arg ...))]
              +        [method
              +         #'(λ args (apply-multimethod method args))]))))
              +
              +(define-syntax define-generic
              +  (syntax-parser
              +    [(_ (method:id arg:id ...+))
              +     (with-syntax ([arity (length (attribute arg))]
              +                   [dispatch-table (generate-temporary #'method)])
              +       (mark-id-as-privileged! #'method)
              +       #'(begin
              +           (define dispatch-table (make-hash))
              +           (define-syntax method (multimethod arity #'dispatch-table))))]))

              The dispatch tables are implemented entirely in terms of Racket’s structure types, so while they can be defined on arbitrary structure types (including ones defined in the Racket standard library), they cannot be defined on primitives such as pairs or vectors. Implementations are registered in the dispatch table using the compile-time information associated with structs’ transformer bindings, and the same information is retrieved from struct instances at runtime to look up the proper implementation to call. Notably, this only works if the struct is #:transparent, or more generally and accurately, if the calling code has access to the struct’s inspector. All structs defined by the struct form from the multimethod module are automatically marked as #:transparent.

              The following code implements defining multimethod instances:

              (begin-for-syntax
              +  (define (assert-privileged-struct! id)
              +    (unless (id-privileged? id)
              +      (raise-syntax-error 'define-instance
              +                          "expected name of struct defined in current module"
              +                          id))))
              +
              +(define-syntax define-instance
              +  (syntax-parser
              +    ; standard (define (proc ...) ...) shorthand
              +    [(_ ((method type:id ...+) . args) body:expr ...+)
              +     #'(define-instance (method type ...) (λ args body ...))]
              +    ; full (define proc lambda-expr) notation
              +    [(_ (method type:id ...+) proc:expr)
              +     (let* ([multimethod (syntax-local-value #'method)]
              +            [privileged? (id-privileged? #'method)])
              +       (unless (or privileged? (ormap id-privileged? (attribute type)))
              +         (assert-privileged-struct! (first (attribute type))))
              +       (with-syntax ([dispatch-table (multimethod-dispatch-table multimethod)]
              +                     [(struct-type-id ...) (map (compose1 first extract-struct-info syntax-local-value)
              +                                                (attribute type))])
              +         #'(let ([struct-types (list struct-type-id ...)])
              +             (hash-set! dispatch-table struct-types proc))))]))

              The resulting implementation is a useful, if certainly incomplete implementation of multimethods in Racket that does not sacrifice the safety provided by racket/generic’s single-dispatch approach.

              Related work, advantages and disadvantages, and areas for future improvement

              As previously mentioned, this implementation of multiple dispatch was inspired by the types of APIs offered by CLOS and Clojure while also maintaining the safety of racket/generic. The inspiration for the safety rules came from GHC’s detection of orphan instances. Although most of the ideas presented above exist in other places, I am unsure if the concept of safety checking has been used before in any dynamically-typed programming languages.

              The primary advantage offered over Racket’s existing generics system is obvious: multiple dispatch. Furthermore, this system can supersede many uses of racket/generic simply by dispatching on a single type. However, the current implementation does not support all of the features of racket/generic, such as supporting non-structure types and allowing fallback implementations. While those are well within the realm of possibility, other things like attaching structure type properties are probably not possible with this approach, so it is unlikely that the existing system could be subsumed by one like this one.

              Additionally, this implementation would almost certainly need numerous improvements before being useful to most programmers:

              • Good error reporting for failure cases. Right now, even something obvious like calling a method on values that do not implement it simply fails with an error produced by hash-ref. In a more interesting sense, using the arity to generate compile-time error messages for define-instance would be a nice improvement.

              • Support for Racket primitive data types. This might require some cooperation from Racket itself to permit an elegant implementation, but they could also just be special-cased. So long as lookup for primitives was done after consulting the main dispatch table, there wouldn’t be any performance hit for non-primitive types.

              • Option to supply fallback implementations. This wouldn’t be too hard at all, though it’s questionable whether or not it would be useful without method groupings like define/generic provides. There would likely also need to be some sort of way to check if a set of values implements a particular method.

              • Better cooperation with structure inspectors to alleviate the need for all structures to be transparent. It’s currently unclear to me how exactly this works and how it should work. There might be a better way to do this without mucking with inspectors.

              • Much more flexible argument lists, including the ability to specify arguments that are not used for dispatch. This is really a pretty fundamental requirement, but the parsing required was significant enough for me to put it off for this initial prototype.

              • Scribble forms to document generic methods and their instances. This is something racket/generic doesn’t have, and it has suffered for it. It would be very nice to have easy documentation forms for multimethods.

              • Proper consideration of struct subtyping. Racket structs support subtyping, which I have not given much thought for this prototype. It is possible that subtyping violates constraints I had assumed would hold, so reviewing the existing code with that context would be useful.

              I’m not sure how much effort is involved in most of the above ideas, and in fact I’m not even completely sure how useful this system is to begin with. I have not found myself reaching much for multiple dispatch in my time as a Racket programmer, but that could simply be because it was previously unavailable. It will be interesting to see if that changes now that I have built this system, even if it is a bit rough around the edges.

              Conclusion

              Despite the lack of need for multiple dispatch to solve most problems, as indicated by its general lack of support in mainstream programming languages, it’s a nice tool to have in the toolbox, and it is asked for in the Racket community from time to time (perhaps due to its familiarity in other parts of the Lisp world). Time will tell if pointing people to something like this will create or stifle interest in multiple dispatch for Racket.

              The source for the multimethod package can be found here if you are at all interested in playing with it yourself.

                \ No newline at end of file diff --git a/blog/2016/06/12/four-months-with-haskell/index.html b/blog/2016/06/12/four-months-with-haskell/index.html new file mode 100644 index 0000000..a876da0 --- /dev/null +++ b/blog/2016/06/12/four-months-with-haskell/index.html @@ -0,0 +1,71 @@ +Four months with Haskell

                Four months with Haskell

                At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.

                Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an incredible programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.

                Dispelling some myths

                Before moving on and discussing my experiences in depth, I want to take a quick detour to dispel some frequent rumors I hear about why Haskell is at least potentially problematic. These are things I hear a lot, and nothing in my experience so far would lead me to believe these are actually true. Ultimately, I don’t want to spend too much time on these—I think that, for the most part, they are nitpicks that people complain about to avoid understanding the deeper and more insidious problems with the language—but I think it’s important to at least mention them.

                Hiring Haskell developers is not hard

                I am on the first Haskell team in my company, and I am among the first Haskell developers we ever hired. Not only were we hiring without much experience with Haskell at all, we explicitly did not want to hire remote. Debate all you like about whether or not permitting remote work is a good idea, but I don’t think anyone would dispute that this constraint makes hiring much harder. We didn’t have any trouble finding a very large stream of qualified applicants, and it definitely seems to have dispelled any fears that we would have trouble finding new candidates in the future.

                Performing I/O in Haskell is easy

                Haskell’s purity is a point of real contention, and it’s one of the most frustrating complaints I often hear about Haskell. It is surprisingly common to hear concerns along the lines of “I don’t want to use Haskell because its academic devotion to purity sounds like it would make it very hard to get anything done”. There are very valid reasons to avoid Haskell, but in practice, I/O is not one of them. In fact, I found that isolating I/O in Haskell was much the same as isolating I/O in every other language, which I need to do anyway to permit unit testing.

                ...you do write deterministic unit tests for your impure logic, right?

                Working with lots of monads is not very difficult

                The “M word” has ended up being a running joke about Haskell that actually ends up coming up fairly rarely within the Haskell community. To be clear, there is no doubt in my mind that monads make Haskell intimidating and provide a steep learning curve for new users. The proliferation of the joke that monads are impossible to explain, to the point of becoming mythologized, is absolutely indicative of a deeper problem about Haskell’s accessibility. However, once people learn the basics about monads, I’ve found that applying them is just as natural as applying any other programming pattern.

                Monads are used to assist the programmer, not impede them, and they really do pay off in practice. When something has a monadic interface, there’s a decent chance I already know what that interface is going to do, and that makes working with lots of different monads surprisingly easy. Admittedly, I do rely very, very heavily on tooling to help me out here, but with things like mouseover type tooltips, I’ve actually found that working with a variety of different monads and monad transformers is actually quite pleasant, and it makes things very readable!

                Haskell: the good parts

                With the disclaimers out of the way, I really just want to gush for a little bit. This is not going to be an objective, reasoned survey of why Haskell is good. I am not even really going to touch upon why types are so great and why purity is so wonderful—I’d love to discuss those in depth, but that’s for a different blog post. For now, I just want to touch upon the real surprises, the real things that made me excited about Haskell in ways I didn’t expect. These are the things that my subjective little experience has found fun.

                Language extensions are Haskell

                There was a time in my life when I spent a lot of time writing C. There are a lot of compilers for C, and they all implement the language in subtly different but often incompatible ways, especially on different platforms. The only way to maintain a modicum of predictability was to adhere to the standards religiously, even when certain GCC or MSVC extensions seem tantalizingly useful. I was actually bitten a few times by real instances where I figured I’d just use a harmless extension that was implemented everywhere, then found out it worked slightly differently across different compilers in a particular edge case. It was a learning experience.

                It seems that this fear provides a very real distrust for using GHC’s numerous language extensions, and indeed, for a long time, I felt that it was probably an admirable goal to stick to Haskell 98 or Haskell 2010 as closely as possible. Sometimes I chose a slightly more verbose solution that was standard Haskell to avoid turning on a trivial extension that would make the code look a little bit cleaner.

                About a year later, I’m finding that attitude was not only a mistake, but it forced me to often completely miss out on a lot of Haskell’s core value. GHC won, and now GHC and Haskell are basically synonymous. With that in mind, the portability concerns of language extensions are a bit of a non-issue, and turning them on is a very good idea! Some extensions are more than a little dangerous, so they cannot all be turned on without thinking, but the question is absolutely not “Is using language extensions a good idea?” and more “Is using this language extension a good idea?”

                This is important, and I bring it up for a reason: so much of the awesomeness of Haskell is locked behind language extensions. Turning a lot of these on is one of the main things that made me really start to see how incredibly powerful Haskell actually is.

                Phantom types

                I’m going to start out by talking about phantom types, which are a pretty simple concept but a powerful one, and they serve as the foundation for a lot of other cool type-level tricks that can make Haskell extremely interesting. The basic idea of a phantom type is simple; it’s a type parameter that isn’t actually used to represent any particular runtime value:

                newtype Id a = Id Text

                This type represents an id for some kind of value, but although the kind of value is specified in the type as the a type parameter, it isn’t actually used anywhere on the data definition—no matter what a is, an Id is just a piece of text. This makes it possible to write functions that operate on specific kinds of ids, and those invariants will be statically checked by the compiler, even though the runtime representation is entirely identical:

                fetchUser :: MonadDB m => Id User -> m User

                Using FlexibleInstances, it’s also possible to create different instances for different kinds of ids. For example, it would be possible to have different Show instances depending on the type of id in question.

                instance Show (Id User) where
                +  show (Id txt) = "user #" <> unpack txt
                +
                +instance Show (Id Post) where
                +  show (Id txt) = "post #" <> unpack txt

                This provides a simple framework for encoding entirely arbitrary information into the type system, then asking the compiler to actually check assertions about that information. This is made even more powerful with some other extensions, which I’ll talk about shortly.

                Letting the compiler write code

                One of the things I really dislike, more than most things, is boilerplate. A little bit of boilerplate is fine—even necessary at times—but as soon as I start wondering if a code generator would improve things, I think the programming language has pretty much failed me.

                I write a lot of Racket because, in a sense, Racket is the ultimate boilerplate killer: the macro system is a first-class code generator integrated with the rest of the language, and it means that boilerplate is almost never an issue. Of course, that’s not always true: sometimes a bit of boilerplate is still necessary because macros cannot deduce enough information about the program to generate the code entirely on their own, and in Haskell, some of that information is actually present in the type system.

                This leads to two absolutely incredible extensions, both of which are simple and related, but which actually completely change how I approach problems when programming. These two extensions are GeneralizedNewtypeDeriving and StandaloneDeriving.

                Newtypes and type safety

                The basic idea is that “newtypes” are just simple wrapper types in Haskell. This turns out to be extremely important when trying to find the value of Haskell because they allow you to harden type safety by specializing types to your domain. For example, consider a type representing a user’s name:

                newtype Name = Name Text

                This type is extremely simple, and in fact isn’t even at all different from a simple Text value with respect to its representation, since all combinations of unicode characters are allowed in a name. Therefore, what’s the point of a separate type? Well, this allows Haskell to introduce actual compilation failures when two different kinds of textual data are mixed. This is not a new idea, and even in languages that don’t support this sort of thing, Joel Spolsky’s old blog post Making Wrong Code Look Wrong describes how it can be done by convention. Still, almost every modern language makes this possible: in C, it would be a single-member struct, in class-based OO languages, it would be a single-member class... this is not a complicated idea.

                The difference lies in its usage. In other languages, this strategy is actually not very frequently employed for the simple reason that it is almost always extremely annoying. You are forced to do tons of wrapping/unwrapping, and at that point it isn’t really clear if you’re even getting all that much value out of the distinction when your first solution to a type mismatch is wrapping or unwrapping the value without a second thought. In Haskell, however, this can be heavily mitigated by asking the compiler to automatically derive typeclass implementations, which allow the unwrapping/wrapping to effectively happen implicitly for a constrained set of operations.

                Using GeneralizedNewtypeDeriving

                Consider the Name type once again, but this time, let’s derive a class:

                newtype Name = Name Text
                +  deriving (IsString)

                The IsString typeclass in Haskell allows custom types to automatically be created from string literals. It is not handled specially by Haskell’s deriving mechanism. Since Text implements IsString, an instance will be generated that simply defers to the underlying type, automatically generating the code to wrap the result up in a Name box at the end. This means that code like this will now just magically work:

                name :: Name
                +name = "Alyssa P. Hacker"

                No boilerplate needs to be written! This is a neat trick, but it actually turns out to be far more useful than that simple example in practice. What really makes this functionality shine is when you want to derive some kinds of functionality but disallow some others. For example, using the text-conversions package, it’s possible to do something like this:

                newtype Id a = Id Text
                +  deriving (Eq, Show, ToText, ToJSON)

                This creates an opaque Id type, but it automatically generates conversions to textual formats. However, it does not automatically create FromText or FromJSON instances, which would be dangerous because decoding Ids can potentially fail. It’s then possible to write out those instances manually to preserve a type safety:

                instance FromText (Maybe (Id a)) where
                +  fromText str = if isValidId str then Just (Id str) else Nothing
                +
                +instance FromJSON (Id a) where
                +  parseJSON (String val) = maybe (fail "invalid id") return (fromText val)
                +  parseJSON _            = fail "invalid id"

                Using StandaloneDeriving

                The ordinary deriving mechanism is extremely useful, especially when paired with the above, but sometimes it is desirable to have a little bit more flexibility. In these cases, StandaloneDeriving can help.

                Take the Id example again: it has a phantom type, and simply adding something like deriving (ToText) with derive ToText instances for all kinds of ids. It is potentially useful, however, to derive instances for more specific id types. Using standalone deriving constructs permits this sort of flexibility.

                deriving instance ToText (Id User)
                +
                +instance ToText (Id Post) where
                +  toText = postIdToText

                This is an example where GHC language extensions end up becoming significantly more than the sum of their parts, which seems to be a fairly frequent realization. The StandaloneDeriving mechanism is a little bit useful without GeneralizedNewtypeDeriving, but when combined, they are incredibly powerful tools for getting a very fine-grained kind of type safety without writing any boilerplate.

                DataKinds are super cool, with caveats

                Phantom types are quite wonderful, but they can only encode types, not arbitrary data. That’s where DataKinds and KindSignatures come in: they allow lifting arbitrary datatypes to the type level so that things that would normally be purely runtime values can be used at compile-time as well.

                The way this works is pretty simple—when you define a datatype, you also define a “datakind”:

                data RegistrationStatus = Registered | Anonymous

                Normally, the above declaration declares a type, RegistrationStatus, and two data constructors, Registered and Anonymous. With DataKinds, it also defines a kind, RegistrationStatus, and two type constructors, Registered and Anonymous.

                If that’s confusing, the way to understand that is to realize there is a sort of natural ordering here: types describe values, and kinds describe types. Therefore, turning on DataKinds “lifts” each definition by a single level, so types become kinds and values become types. This permits using these things at the type level:

                newtype UserId (s :: RegistrationStatus) = UserId Text

                In this example, UserId still has a single phantom type variable, s, but this time it is constrained to the RegistrationStatus kind. Therefore, it can only be Registered or Anonymous. This cooperates well with the aforementioned StandaloneDeriving mechanism, and it mostly provides a convenient way to constrain type variables to custom kinds.

                In general, DataKinds is a much more powerful extension, allowing things like type-level natural numbers or strings, which can be used to perform actual type-level computation (especially in combination with TypeFamilies) or a sort of metaprogramming. In some cases, they can even be used to implement things emulating things you can do with dependent types.

                I think DataKinds are a very cool Haskell extension, but there are a couple caveats. One of the main ones is how new kinds are defined: DataKinds “hijacks” the existing datatype declaration syntax by making every single datatype declaration define a type and a kind. This is a little confusing, and it would be nice if a different syntax was used so that each could be defined independently.

                Similarly, it seems that a lot of work is being done to allow using runtime values at the type level, but I wonder if people will ever need to use, say, runtime values at the kind level. This immediately evokes thoughts of Racket’s phase-based macro system, and I wonder if some of this duplication would be unnecessary with something similar.

                Food for thought, but overall, DataKinds are a very nice addition to help with precisely and specifically typing particular problems.

                Typeclasses can emulate effects

                This is something that I’ve found interesting in my time writing Haskell because I have no idea if it’s idiomatic or not, but it seems pretty powerful. The initial motivator for this idea was figuring out how to test our code without constantly dropping into IO.

                More generally, we wanted to be able to unit test by “mocking” out collaborators, as it would be described in object oriented programming. I was always semi-distrustful of mocking, and indeed, it seems likely that it is heavily abused in certain circles, but I’ve come to appreciate the need that sometimes it is important to stub things out, even in pure code.

                As an example, consider some code that needs access to the current time. This is something that would normally require IO, but we likely want to be able to use the value in a pure context without “infecting” the entire program with IO types. In Haskell, I have generally seen three ways of handling this sort of thing:

                1. Just inject the required values into the function and produce them “higher up” where I/O is okay. If threading the value around becomes too burdensome, use a Reader monad.

                2. Use a free monad or similar to create a pure DSL of sorts, then write interpreters for various implementations, one of which uses IO.

                3. Create custom monadic typeclasses that provide interfaces to the functionality you want to perform, then create instances, one of which is an instance over IO.

                This last approach seems to be less common in Haskell, but it’s the approach we took, and it seems to work out remarkably well. Returning to the need to get the current time, we could pretty easily write such a typeclass to encode that need:

                class Monad m => CurrentTime m where
                +  getCurrentTime :: m UTCTime

                Now we can write functions that use the current time:

                validateToken :: CurrentTime m => Token -> m Bool
                +validateToken tok = do
                +  currentTime <- getCurrentTime
                +  return (tokenExpirationDate tok > currentTime)

                Now, we can write instances for CurrentTime that will allow us to run the same code in different contexts:

                newtype AppM a = AppM { runAppM :: IO a }
                +  deriving (Functor, Applicative, Monad, MonadIO)
                +
                +newtype TestM a = TestM (Identity a)
                +  deriving (Functor, Applicative, Monad)
                +
                +runTestM :: TestM a -> a
                +runTestM (TestM x) = runIdentity x
                +
                +instance CurrentTime AppM where
                +  getCurrentTime = liftIO Data.Time.Clock.getCurrentTime
                +
                +instance CurrentTime TestM where
                +  getCurrentTime = return $ posixSecondsToUTCTime 0

                Where this really starts to shine is when adding additional effects. For example, the above token validation function might also need information about some kind of secret used for signing. Under this model, it’s just another typeclass:

                class Monad m => TokenSecret m where
                +  getTokenSecret :: m Secret
                +
                +validateToken :: (CurrentTime m, TokenSecret m) => Token -> m Bool
                +validateToken tok = do
                +  currentTime <- getCurrentTime
                +  secret <- getTokenSecret
                +  return (tokenExpirationDate tok > currentTime
                +       && verifySignature tok secret)

                Of course, so far all of these functions have been extremely simple, and we’ve basically been using them as a glorified reader monad. In practice, though, we use this pattern for lots more than just retrieving values. For example, we might have a typeclass for database interactions:

                class Monad m => Persistence m where
                +  fetchUser :: Id User -> m (Maybe User)
                +  insertUser :: User -> m (Either PersistenceError (Id User))

                With all of this done, it becomes incredibly easy to see which functions are using which effects:

                postUsers
                +  :: (CurrentTime m, Persistence m, TokenSecret m)
                +  => User -> m Response
                +postUsers = ...
                +
                +getHealthcheck
                +  :: CurrentTime m
                +  => m Response
                +getHealthcheck = ...

                There’s no need to perform any lifting, and this all seems to scale quite nicely. We’ve written some additional utilities to help write tests against functions using these kinds of monadic interfaces, and even though there’s a little bit of annoying boilerplate in a few spots, overall it seems to work quite elegantly.

                I’m not entirely sure how common this is in the Haskell community, but it’s certainly pretty neat how easy it is to get nearly all of the benefits of effect types in other languages simply by composing some of Haskell’s simplest features.

                Atom’s ide-haskell tooling is invaluable

                Alright, so, confession time: I don’t use Emacs.

                I know, I know, how is that possible? I write Lisp, after all. Well, honestly, I tried picking it up a number of times, but none of those times did I get far enough to ditch my other tools. For Racket work, I use DrRacket, but for almost everything else, I use Atom.

                Atom has a lot of flaws, but it’s also pretty amazing in places, and I absolutely love the Haskell tooling written by the wonderful atom-haskell folks. I use it constantly, and even though it doesn’t always work perfectly, it works pretty well. When it has problems, I’ve at least figured out how to get it working correctly.

                This is probably hard to really explain without seeing it for yourself, but I’ve found that I basically depend on this sort of tooling to be fully productive in Haskell, and I have no problem admitting that. The ability to get instant feedback about type errors tied to visual source locations, to be able to directly manipulate the source by selecting expressions and getting type information, and even the option to get inline linter suggestions means I spend a lot less time glancing at the terminal, and even less time in the REPL.

                The tooling is far from perfect, and it leaves a lot to be desired in places (the idea of using that static information for automated, project-wide refactoring a la Java is tantalizing), but most of those things are ideas of what amazing things could be, not broken or missing essentials. I am pretty satisfied with ide-haskell right now, and I can only hope it continues to get better and better.

                Frustrations, drawbacks, and pain points

                Haskell is not perfect. In fact, far from it. There is a vast array of little annoyances that I have with the language, as is the case with any language. Still, there are a few overarching problems that I would really like to at least mention. These are the biggest sources of frustration for me so far.

                Purity, failure, and exception-handling

                One of Haskell’s defining features is its purity—I don’t think many would disagree with that. Some people consider it a drawback, others consider it one of its greatest boons. Personally, I like it a lot, and I think one of the best parts about it is how it requires the programmer to be incredibly deliberate about failure.

                In many languages, when looking up a value from a container where the key doesn’t exist, there are really two ways to go about expressing this failure:

                1. Throw an exception.

                2. Return null.

                The former is scary because it means any call to any function can make the entire program blow up, and it’s often impossible to know which functions even have the potential to throw. This creates a certain kind of non-local control flow that can sometimes cause a lot of unpredictability. The second option is much the same, especially when any value in a program might be null; it just defers the failure.

                In languages with option types, this is somewhat mitigated. Java now has option types, too, but they are still frequently cumbersome to use because there is nothing like monads to use to simply chain operations together. Haskell, in comparison, has an incredible complement of tools to simply handle errors without a whole lot of burden on the programmer, and I have found that, in practice, this is actually helpful and I really do write better error-handling code.

                First, the good parts

                I have seen a comparison drawn between throwing checked exceptions and returning Maybe or Either types, but in practice the difference is massive. Handling checked exceptions is a monotonous chore because they are not first-class values, they are actually entirely separate linguistic constructs. Consider a library that throws a LibraryException, and you want to wrap that library and convert those exceptions to ApplicationExceptions. Well, have fun writing this code dozens of times:

                try {
                +  x = doSomething();
                +} catch (LibraryException ex) {
                +  throw ApplicationException.fromLibraryException(ex);
                +}
                +
                +// ...
                +
                +try {
                +  y = doSomethingElse();
                +} catch (LibraryException ex) {
                +  throw ApplicationException.fromLibraryException(ex);
                +}

                In Haskell, failure is just represented by first-class values, and it’s totally possible to write helper functions to abstract over that kind of boilerplate:

                libraryToApplication :: LibraryError -> ApplicationError
                +libraryToApplication = ...
                +
                +liftLibrary :: Either LibraryError a -> Either ApplicationError a
                +liftLibrary = mapLeft libraryToApplication

                Now, that same boilerplate-y code becomes nearly invisible:

                x <- liftLibrary doSomething
                +
                +-- ...
                +
                +y <- liftLibrary doSomethingElse

                This might not seem like much, but it really cuts down on the amount of visual noise, which ends up making all the difference. Boilerplate incurs a cost much bigger than simply taking the time to type it all out (though that’s important, too): the cognitive overhead of parsing which parts of a program are boilerplate has a significant impact on readability.

                So what’s the problem?

                If error handling is so great in Haskell, then why am I putting it under the complaints section? Well, it turns out that not everyone seems to think it’s as great as I make it out to be because people seem to keep writing Haskell APIs that throw exceptions!

                Despite what some purists would have you believe, Haskell has exceptions, and they are not uncommon. Lots of things can throw exceptions, some of which are probably reasonable. Failing to connect to a database is a pretty catastrophic error, so it seems fair that it would throw. On the other hand, inserting a duplicate record is pretty normal operation, so it seems like that should not throw.

                I mostly treat exceptions in Haskell as unrecoverable catastrophes. If I throw an error in my code, I do not intend to catch it. That means something horrible happened, and I just want that horribleness to show up in a log somewhere so I can fix the problem. If I care about failure, there are better ways to handle that failure gracefully.

                It’s also probably worth noting that exceptions in Haskell can be thrown from anywhere, even pure code, but can only be caught within the IO monad. This is especially scary, but I’ve seen it happen in actual libraries out in the wild, even ones that the entire Haskell ecosystem is built on. One of the crowning examples of this is the text package, which provides a function called decodeUtf8 to convert bytestrings into text. Its type is very simple:

                decodeUtf8 :: ByteString -> Text

                But wait, what if the bytestring is not actually a valid UTF-8 string?

                Boom. There goes the application.

                Okay, okay, well, at least the text package provides another function, this one called decodeUtf8', which returns an Either. This is good, and I’ve trained myself to only ever use decodeUtf8', but it still has some pretty significant problems:

                • The safe version of this function is the “prime” version, rather than the other way around, which encourages people to use the unsafe one. Ideally, the unsafe one should be explicitly labeled as such... maybe call it unsafeDecodeUtf8?

                • This is not a hypothetical problem. When using a Haskell JWT library, we found a function that converts a string into a JWT. Since not all strings are JWTs, the library intelligently returns a Maybe. Therefore, we figured we were safe.

                  A couple weeks later, we found that providing this function with invalid data was returning HTTP 500 errors. Why? Our error handling was meticulous! Well, the answer was a decodeUtf8 call, hidden inside of the JWT library. This is especially egregious, given that the API it exposed returned a Maybe anyway! It would have been trivial to use the safe version there, instead, but the poor, misleading name led the library developer to overlook the bug lurking in the otherwise innocuous function.

                  Even worse, this function was totally pure, and we used it in pure code, so we could not simply wrap the function and catch the exception. We had two options: use unsafePerformIO (yuck!) or perform a check before handing the data to the buggy function. We chose the latter, but in some cases, I imagine that could be too difficult to do in order to make it feasible.

                The point I’m trying to make is that this is a real problem, and it seems to me that throwing exceptions invalidates one of the primary advantages of Haskell. It disappointed me to realize that a significant amount of code written by FP Complete, one of the primary authors of some of the most important “modern Haskell” code in existence (including Stack), seem to very frequently expose APIs that will throw.

                I’m not sure how much of this stems from a fundamental divide in the Haskell ecosystem and how much it is simply due to Michael Snoyman’s coding style, given that he is the primary author of a number of these tools and libraries that seem very eager to throw exceptions. As just one example of a real situation in which we were surprised by this behavior, we used Snoyman’s http-client library and found that it mysteriously throws upon nearly any failure state:

                A note on exceptions: for the most part, all actions that perform I/O should be assumed to throw an HttpException in the event of some problem, and all pure functions will be total. For example, withResponse, httpLbs, and BodyReader can all throw exceptions.

                This doesn’t seem entirely unreasonable—after all, isn’t a failure to negotiate TLS fairly catastrophic?—until you consider our use case. We needed to make a subrequest during the extent of another HTTP request to our server, and if that subrequest fails, we absolutely need to handle that failure gracefully. Of course, this is not terrible given that we are in IO so we can actually catch these exceptions, but since this behavior was only noted in a single aside at the top of the documentation, we didn’t realize we were forgetting error handling until far too late and requests were silently failing.

                Exceptions seem to devalue one of the most powerful concepts in Haskell: if I don’t consider all the possibilities, my code does not compile. In practice, when working with APIs that properly encode these possibilities into the type system, this value proposition seems to be real. I really do find myself writing code that works correctly as soon as it compiles. It’s almost magical.

                Using exceptions throws that all out the window, and I wish the Haskell ecosystem was generally more cautious about when to use them.

                The String problem

                I sort of alluded to this a tiny bit in the last section, and that is probably indicative of how bad this issue is. I’m just going to be blunt: In Haskell, strings suck.

                This is always a bit of an amusing point whenever it is discussed because of how silly it seems. Haskell is a research language with a cutting-edge type system and some of the fanciest features of any language in existence. When everyday programming might use things like “profunctors”, “injective type families”, and “generalized algebraic datatypes”, you would think that dealing with strings would be a well-solved problem.

                But it isn’t. Haskell libraries frequently use not one, not two, but five kinds of strings. Let’s list them off, shall we?

                • First off, there’s the built-in String type, which is actually an alias for the [Char] type. For those not intimately familiar with Haskell, that’s a linked list of characters. As Stephen Diehl recently put it in a blog post describing the disaster that is Haskell string types:

                  This is not only a bad representation, it’s quite possibly the least efficient (non-contrived) representation of text data possible and has horrible performance in both time and space. And it’s used everywhere in Haskell.

                  The point is, it’s really bad. This type is not a useful representation for textual data in practical applications.

                • Moving on, we have a fairly decent type, Text, which comes from Data.Text in the text package. This is a decent representation of text, and it’s probably the one that everything should use. Well, maybe. Because Text comes in two varieties: lazy and strict. Nobody seems to agree on which of those two should be used, though, and they are totally incompatible types: functions that work with one kind of text won’t work with the other. You have to manually convert between them.

                • Finally, we have ByteString, which is horribly misnamed because it really isn’t a string at all, at least not in the textual sense. A better name for this type would have simply been Bytes, which sounds a lot scarier. And that would be good, because data typed as a ByteString is as close as you can get in Haskell to not assigning a type at all: a bytestring holds arbitrary bytes without assigning them any meaning whatsoever!

                  Or at least, that’s the intention. The trouble is that people don’t treat bytestrings like that—they just use them to toss pieces of text around, even when those pieces of text have a well-defined encoding and represent textual data. This leads to the decodeUtf8 problem mentioned above, but it’s bigger than that because it often ends up with some poor APIs that assign some interpretation to ByteString data without assigning it a different type.

                  Again, this is throwing away so much of Haskell’s safety. It would be like using Int to keep track of boolean data (“just use 0 and 1!”) or using empty and singleton lists instead of using Maybe. When you use the precise type, you encode invariants and contracts into statically-checked assertions, but when you use general types like ByteString, you give that up.

                  Oh, and did I mention that ByteStrings also come in incompatible lazy and strict versions, too?

                So, obviously, the answer is to just stop using the bad types and to just use (one kind of) Text everywhere. Great! Except that the other types are totally inescapable. The entire standard library uses String exclusively—after all, text is a separate package—and small libraries often use String instead of text because they have no need to bring in the dependency. Of course, this just means every real application pays the performance hit of converting between all these different kinds of strings.

                Similarly, those that do use Text often use different kinds of text, so code ends up littered with fromStrict or toStrict coercions, which (again) have a cost. I’ve already ranted enough about ByteString, but basically, if you’re using ByteString in your API to pass around data that is semantically text, you are causing me pain. Please stop.

                It seems that the way Data.Text probably should have been designed was by making Text a typeclass, then making the lazy and strict implementations instances of that typeclass. Still, the fact that both of them exist would always cause problems. I’m actually unsure which one is the “correct” choice—I don’t know enough about how the two perform in practice—but it seems likely that picking either one would be a performance improvement over the current system, which is constantly spending time converting between the two.

                This issue has been ranted about plenty, so I won’t ramble on, but if you’re designing new libraries, please, please use Text. Your users will thank you.

                Documentation is nearly worthless

                Finally, let’s talk about documentation.

                One of my favorite programming languages is Racket. Racket has a documentation tool called Scribble. Scribble is special because it is a totally separate domain-specific language for writing documentation, and it makes it fun and easy to write good explanations. There are even forms for typesetting automatically-rendered examples that look like a REPL. If the examples ever break or become incorrect, the docs don’t even compile.

                All of the Racket core library documentation makes sure to set a good example about what good documentation should look like. The vast majority of the documentation is paragraphs of prose and simple but practical examples. There are also type signatures (in the form of contracts), and those are super important, but they are so effective because of how the prose explains what each function does, when to use it, why you’d use it, and why you wouldn’t use it.

                Everything is cross-referenced automatically. The documentation is completely searchable locally out of the box. As soon as you install a package, its docs are automatically indexed. User-written libraries tend to have pretty good docs, too, because the standard libraries set such a good example and because the tools are so fantastic. Racket docs are really nice, and they’re so good they actually make things like Stack Overflow or even Google mostly irrelevant. It’s all there in the manual.

                Haskell documentation is the opposite of everything I just said.

                • The core libraries are poorly documented. Most functions include a sentence of description, and almost none include examples. At their worst, the descriptions simply restate the type signature.

                • Third-party libraries’ documentation is even worse, going frequently completely undocumented and actually only including type signatures and nothing else.

                • Haddock is an incredibly user-hostile tool for writing anything other than tiny snippets of documentation and is not very good at supporting prose. Notably, Haddock’s documentation is not generated using Haddock (and it still manages to be almost unusable). Forcing all documentation into inline comments makes users unlikely to write much explanation, and there is no ability for abstraction.

                • Reading documentation locally is very difficult because there is no easy way to open documentation for a particular package in a web browser, and it’s certainly not searchable. This is especially ridiculous given that Hoogle exists, which is one of best ways to search API docs in existence. There should be a stack hoogle command that just opens a Hoogle page for all locally-installed packages and Just Works, but there isn’t.

                • Most valuable information exists outside of documentation, so Google becomes a go-to immediately after a quick glance at the docs, and information is spread across blog posts, mailing lists, and obscure reddit posts.

                This is a problem that cannot be fixed by just making Haddock better, nor can it be fixed simply by improving the existing standard library documentation. There is a fundamental problem with Haskell documentation (which, to be completely fair, is not unique to Haskell), which is that its tools do not support anything more than API docs.

                Good documentation is so much more than “here’s what this function does”; it’s about guides and tutorials and case studies and common pitfalls. This is documentation for someone new to lenses. This is not. Take note of the difference.

                Conclusion and other thoughts

                Haskell is an incredible programming platform, and indeed, it is sometimes mind-boggling how complete it is. It also has a lot of rough edges, sometimes in places that feel like they need a lot more care, or perhaps they’re even simply unfinished.

                I could spend weeks writing about all the things I really like or dislike about the language, discussing in fine detail all the things that have made me excited or all the little bits that have made me want to tear my hair out. Heck, I could probably spend a month writing about strings alone. That’s not the point, though... I took a risk with Haskell, and it’s paid off. I’m not yet sure exactly how I feel about it, or when I would chose it relative to other tools, but it is currently very high on my list of favorite technologies.

                I did not come to Haskell with a distaste for static typing, despite the fact that I write so much Racket, a dynamically typed language (by default, at least). I don’t really use Typed Racket, and despite my love for Haskell and its type system, I am not sure I will use much more of it than I did before. Haskell and Racket are very different languages, which is justified in some places and probably sort of circumstantial in others.

                The future of Haskell seems bright, and a lot of the changes in the just-released GHC 8 are extremely exciting. I did not list records as a pain point because the changes in GHC 8 appear to make them a lot more palatable, although whether or not they solve that problem completely remains to be seen. I will absolutely continue to write Haskell and push it to its limits where I can, and hopefully try and take as much as I can from it along the way.

                  \ No newline at end of file diff --git a/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/index.html b/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/index.html new file mode 100644 index 0000000..2a606b4 --- /dev/null +++ b/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/index.html @@ -0,0 +1,21 @@ +Climbing the infinite ladder of abstraction

                  Climbing the infinite ladder of abstraction

                  I started programming in elementary school.

                  When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to solve the general problem. When I learned about programming, I was immediately hooked: it was so easy to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.

                  Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of abstraction.

                  The brick wall of inexpressiveness

                  When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:

                  public String getName() {
                  +  return this.name;
                  +}
                  +
                  +public void setName(String name) {
                  +  this.name = name;
                  +}

                  This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.

                  It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The whole reason I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.

                  When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.

                  Both of these languages were flawed, as all languages are, but they opened my mind to the idea that programming languages could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.

                  Discovering Lisp

                  Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.

                  This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?

                  The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a highly dynamic Lisp with a prototypal object system called Sol. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.

                  Unfortunately, it was also abysmally slow.

                  I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called Racket. At about the same time, someone pointed me to a totally different language called Haskell. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.

                  A journey into complexity

                  Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned so much more since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.

                  I’ve also started realizing something else, though: the languages I’ve settled into are really complicated.

                  When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.

                  Why?

                  Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”

                  Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?

                  I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.

                  Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I still have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?

                  Improvement, but never mastery

                  Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.

                  For example I am pretty confident that I get JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques within the language, not entirely new linguistic constructs.

                  On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about TypeInType? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do not have a good grasp on all the intricacies of Racket’s macro system.

                  This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out DataKinds, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.

                  Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is terrible, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.

                  Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even more advanced things, and still, none of them truly solve my problems.

                  Ultimately, it all has (at least a little) value

                  It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.

                  The scariest part of all is that I think it’s completely worthwhile.

                  So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs seem unnecessarily complicated, they seem increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.

                  The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.

                  Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:

                  10 INPUT "What is your name: "; U$
                  +20 PRINT "Hello "; U$

                  On the other hand, very few would probably understand this one:

                  -- | A class for categories.
                  +--   id and (.) must form a monoid.
                  +class Category cat where
                  +    -- | the identity morphism
                  +    id :: cat a a
                  +
                  +    -- | morphism composition
                  +    (.) :: cat b c -> cat a b -> cat a c

                  Yet very few new programs are being written in BASIC, and lots are being written in Haskell.

                  Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:

                  export const composeWithProps = curry((a, parentProps, b) => {
                  +  const composed = childProps =>
                  +    createElement(a, parentProps, createElement(b, omit(['children'], childProps), childProps.children));
                  +  // give the composed component a pretty display name for debugging
                  +  composed.displayName = `Composed(${getDisplayName(a)}, ${getDisplayName(b)})`;
                  +  return composed;
                  +});

                  Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.

                  That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an extremely productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.

                  Reactionary anti-intellectualism and the search for moderation

                  I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. I recently tweeted a picture of some rather dense mathematics from a paper I’d read, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.

                  Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like map should be banned because they are too confusing and not immediately self-explanatory.

                  I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.

                  Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern technology possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.

                  Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.

                  I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.

                  Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.

                    \ No newline at end of file diff --git a/blog/2016/08/24/understanding-the-npm-dependency-model/index.html b/blog/2016/08/24/understanding-the-npm-dependency-model/index.html new file mode 100644 index 0000000..e464cf1 --- /dev/null +++ b/blog/2016/08/24/understanding-the-npm-dependency-model/index.html @@ -0,0 +1,36 @@ +Understanding the npm dependency model

                    Understanding the npm dependency model

                    ⦿ javascript

                    Currently, npm is the package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like Bower are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects you as a user or package developer.

                    First, the basics

                    At a high level, npm is not too dissimilar from other package managers for programming languages: packages depend on other packages, and they express those dependencies with version ranges. npm happens to use the semver versioning scheme to express those ranges, but the way it performs version resolution is mostly immaterial; what matters is that packages can depend on ranges rather than specific versions of packages.

                    This is rather important in any ecosystem, since locking a library to a specific set of dependencies could cause significant problems, but it’s actually much less of a problem in npm’s case compared to other, similar package systems. Indeed, it is often safe for a library author to pin a dependency to a specific version without affecting dependent packages or applications. The tricky bit is determining when this is safe and when it’s not, and this is what I so frequently find that people get wrong.

                    Dependency duplication and the dependency tree

                    Most users of npm (or at least most package authors) eventually learn that, unlike other package managers, npm installs a tree of dependencies. That is, every package installed gets its own set of dependencies rather than forcing every package to share the same canonical set of packages. Obviously, virtually every single package manager in existence has to model a dependency tree at some point, since that’s how dependencies are expressed by programmers.

                    For example, consider two packages, foo and bar. Each of them have their own set of dependencies, which can be represented as a tree:

                    foo
                    +├── hello ^0.1.2
                    +└── world ^1.0.7
                    +
                    +bar
                    +├── hello ^0.2.8
                    +└── goodbye ^3.4.0
                    +

                    Imagine an application that depends on both foo and bar. Obviously, the world and goodbye dependencies are totally unrelated, so how npm handles them is relatively uninteresting. However, consider the case of hello: both packages require conflicting versions.

                    Most package managers (including RubyGems/Bundler, pip, and Cabal) would simply barf here, reporting a version conflict. This is because, in most package management models, only one version of any particular package can be installed at a time. In that sense, one of the package manager’s primary responsibilities is to figure out a set of package versions that will satisfy every version constraint simultaneously.

                    In contrast, npm has a somewhat easier job: it’s totally okay with installing different versions of the same package because each package gets its own set of dependencies. In the aforementioned example, the resulting directory structure would look something like this:

                    node_modules/
                    +├── foo/
                    +│   └── node_modules/
                    +│       ├── hello/
                    +│       └── world/
                    +└── bar/
                    +    └── node_modules/
                    +        ├── hello/
                    +        └── goodbye/
                    +

                    Notably, the directory structure very closely mirrors the actual dependency tree. The above diagram is something of a simplification: in practice, each transitive dependency would have its own node_modules directory and so on, but the directory structure can get pretty messy pretty quickly. (Furthermore, npm 3 performs some optimizations to attempt to share dependencies when it can, but those are ultimately unnecessary to actually understanding the model.)

                    This model is, of course, extremely simple. The obvious effect is that every package gets its own little sandbox, which works absolutely marvelously for utility libraries like ramda, lodash, or underscore. If foo depends on ramda@^0.19.0 but bar depends on ramda@^0.22.0, they can both coexist completely peacefully without any problems.

                    At first blush, this system is obviously better than the alternative, flat model, so long as the underlying runtime supports the required module loading scheme. However, it is not without drawbacks.

                    The most apparent downside is a significant increase in code size, given the potential for many, many copies of the same package, all with different versions. An increase in code size can often mean more than just a larger program—it can have a significant impact on performance. Larger programs just don’t fit into CPU caches as easily, and merely having to page a program in and out can significantly slow things down. That’s mostly just a tradeoff, though, since you’re sacrificing performance, not program correctness.

                    The more insidious problem (and the one that I see crop up quite a lot in the npm ecosystem without much thought) is how dependency isolation can affect cross-package communication.

                    Dependency isolation and values that pass package boundaries

                    The earlier example of using ramda is a place where npm’s default dependency management scheme really shines, given that Ramda just provides a bunch of plain ol’ functions. Passing these around is totally harmless. In fact, mixing functions from two different versions of Ramda would be totally okay! Unfortunately, not all cases are nearly that simple.

                    Consider, for a moment, react. React components are very much not plain old data; they are complex values that can be extended, instantiated, and rendered in a variety of ways. React represents component structure and state using an internal, private format, using a mixture of carefully arranged keys and values and some of the more powerful features of JavaScript’s object system. This internal structure might very well change between React versions, so a React component defined with react@0.3.0 likely won’t work quite right with react@15.3.1.

                    With that in mind, consider two packages that define their own React components and export them for consumers to use. Looking at their dependency tree, we might see something like this:

                    awesome-button
                    +└── react ^0.3.0
                    +
                    +amazing-modal
                    +└── react ^15.3.1
                    +

                    Given that these two packages use wildly different versions of React, npm would give each of them their own copy of React, as requested, and packages would happily install. However, if you tried to use these components together, they wouldn’t work at all! A newer version of React simply cannot understand an old version’s component, so you would get a (likely confusing) runtime error.

                    What went wrong? Well, dependency isolation works great when a package’s dependencies are purely implementation details, never observable from outside of a package. However, as soon as a package’s dependency becomes exposed as part of its interface, dependency isolation is not only subtly wrong, it can cause complete failure at runtime. These are cases when traditional dependency management are much better—they will tell you as soon as you attempt to install two packages that they just don’t work together, rather than waiting for you to figure that out for yourself.

                    This might not sound too bad—after all, JavaScript is a very dynamic language, so static guarantees are mostly few and far between, and your tests should catch these problems should they arise—but it can cause unnecessary issues when two packages can theoretically work together fine, but because npm assigned each one its own copy of a particular package (that is, it wasn’t quite smart enough to figure out it could give them both the same copy), things break down.

                    Looking outside of npm specifically and considering this model when applied to other languages, it becomes increasingly clear that this won’t do. This blog post was inspired by a Reddit thread discussing the npm model applied to Haskell, and this flaw was touted as a reason why it couldn’t possibly work for such a static language.

                    Due to the way the JavaScript ecosystem has evolved, it’s true that most people can often get away with this subtle potential for incorrect behavior without any problems. Specifically, JavaScript tends to rely on duck typing rather than more restrictive checks like instanceof, so objects that satisfy the same protocol will still be compatible, even if their implementations aren’t quite the same. However, npm actually provides a robust solution to this problem that allows package authors to explicitly express these “cross-interface” dependencies.

                    Peer dependencies

                    Normally, npm package dependencies are listed under a "dependencies" key in the package’s package.json file. There is, however, another, less-used key called "peerDependencies", which has the same format as the ordinary dependencies list. The difference shows up in how npm performs dependency resolution: rather than getting its own copy of a peer dependency, a package expects that dependency to be provided by its dependent.

                    This effectively means that peer dependencies are effectively resolved using the “traditional” dependency resolution mechanism that tools like Bundler and Cabal use: there must be one canonical version that satisfies everyone’s constraint. Since npm 3, things are a little bit less straightforward (specifically, peer dependencies are not automatically installed unless a dependent package explicitly depends on the peer package itself), but the basic idea is the same. This means that package authors must make a choice for each dependency they install: should it be a normal dependency or a peer dependency?

                    This is where I think people tend to get a little lost, even those familiar with the peer dependency mechanism. Fortunately, the answer is relatively simple: is the dependency in question visible in any place in the package’s interface?

                    This is sometimes hard to see in JavaScript because the “types” are invisible; that is, they are dynamic and rarely explicitly written out. However, just because the types are dynamic does not mean they are not there at runtime (and in the heads of various programmers), so the rule still holds: if the type of a function in a package’s public interface somehow depends on a dependency, it should be a peer dependency.

                    To make this a little more concrete, let’s look at a couple of examples. First off, let’s take a look at some simple cases, starting with some uses of ramda:

                    import { merge, add } from 'ramda'
                    +
                    +export const withDefaultConfig = (config) =>
                    +  merge({ path: '.' }, config)
                    +
                    +export const add5 = add(5)

                    The first example here is pretty obvious: in withDefaultConfig, merge is used purely as an implementation detail, so it’s safe, and it’s not part of the module’s interface. In add5, the example is a little trickier: the result of add(5) is a partially-applied function created by Ramda, so technically, a Ramda-created value is a part of this module’s interface. However, the contract add5 has with the outside world is simply that it is a JavaScript function that adds five to its argument, and it doesn’t depend on any Ramda-specific functionality, so ramda can safely be a non-peer dependency.

                    Now let’s look at another example using the jpeg image library:

                    import { Jpeg } from 'jpeg'
                    +
                    +export const createSquareBuffer = (size, cb) =>
                    +  createSquareJpeg(size).encode(cb)
                    +
                    +export const createSquareJpeg = (size) =>
                    +  new Jpeg(Buffer.alloc(size * size, 0), size, size)

                    In this case, the createSquareBuffer function invokes a callback with an ordinary Node.js Buffer object, so the jpeg library is an implementation detail. If that were the only function exposed by this module, jpeg could safely be a non-peer dependency. However, the createSquareJpeg function violates that rule: it returns a Jpeg object, which is an opaque value with a structure defined exclusively by the jpeg library. Therefore, a package with the above module must list jpeg as a peer dependency.

                    This sort of restriction works in reverse, too. For example, consider the following module:

                    import { writeFile } from 'fs'
                    +
                    +export const writeJpeg = (filename, jpeg, cb) =>
                    +  jpeg.encode((image) => fs.writeFile(filename, image, cb))

                    The above module does not even import the jpeg package, yet it implicitly depends on the encode method of the Jpeg interface. Therefore, despite not even explicitly using it anywhere in the code, a package containing the above module should include jpeg as a peer dependency.

                    They key is to carefully consider what contract your modules have with their dependents. If those contracts involve other packages in any way, they should be peer dependencies. If they don’t, they should be ordinary dependencies.

                    Applying the npm model to other programming languages

                    The npm model of package management is more complicated than that of other languages, but it provides a real advantage: implementation details are kept as implementation details. In other systems, it’s quite possible to find yourself in “dependency hell”, when you personally know that the version conflict reported by your package manager is not a real problem, but because the package system must pick a single canonical version, there’s no way to make progress without adjusting code in your dependencies. This is extremely frustrating.

                    This sort of dependency isolation is not the most advanced form of package management in existence—indeed, far from it—but it’s definitely more powerful than most other mainstream systems out there. Of course, most other languages could not adopt the npm model simply by changing the package manager: having a global package namespace can prevent multiple versions of the same package being installed at a runtime level. The reason npm is able to do what it does is because Node itself supports it.

                    That said, the dichotomy between peer and non-peer dependencies is a little confusing, especially to people who aren’t package authors. Figuring out which packages need to go in which group is not always obvious or trivial. Fortunately, other languages might be able to help.

                    Returning to Haskell, its strong static type system would potentially allow this distinction to be detected entirely automatically, and Cabal could actually report an error when a package used in an exposed interface was not listed as a peer dependency (much like how it currently prevents importing a transitive dependency without explicitly depending on it). This would allow helper function packages to keep on being implementation details while still maintaining strong interface safety. This would likely take a lot of work to get just right—managing the global nature of typeclass instances would likely make this much more complicated than a naïve approach would accommodate—but it would add a nice layer of flexibility that does not currently exist.

                    From the perspective of JavaScript, npm has demonstrated that it can be a capable package manager, despite the monumental burden placed upon it by the ever-growing, ever-changing JS ecosystem. As a package author myself, I would implore other users to carefully consider the peer dependencies feature and work hard to encode their interfaces’ contracts using it—it’s a commonly misunderstood gem of the npm model, and I hope this blog post helped to shed at least a little more light upon it.

                      \ No newline at end of file diff --git a/blog/2016/10/03/using-types-to-unit-test-in-haskell/index.html b/blog/2016/10/03/using-types-to-unit-test-in-haskell/index.html new file mode 100644 index 0000000..e469db9 --- /dev/null +++ b/blog/2016/10/03/using-types-to-unit-test-in-haskell/index.html @@ -0,0 +1,160 @@ +Using types to unit-test in Haskell

                      Using types to unit-test in Haskell

                      ⦿ haskell, testing

                      Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.

                      Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.

                      First, an aside on testing philosophy

                      Testing methodology is a controversial topic within the larger programming community, and there are a multitude of different approaches. This blog post is about unit testing, an already nebulous term with a number of different definitions. For the purposes of this post, I will define a unit test as a test that stubs out collaborators of the code under test in some way. Accomplishing that in Haskell is what this is primarily about.

                      I want to be clear that I do not think that unit tests are the only way to write tests, nor the best way, nor even always an applicable way. Depending on your domain, rigorous unit testing might not even make sense, and other forms of tests (end-to-end, integration, benchmarks, etc.) might fulfill your needs.

                      In practice, though, implementing those other kinds of tests seems to be well-documented in Haskell compared to pure, object-oriented style unit testing. As my Haskell applications have grown, I have found myself wanting a more fine-grained testing tool that allows me to both test a piece of my codebase in isolation and also use my domain-specific types. This blog post is about that.

                      With that disclaimer out of the way, let’s talk about testing in Haskell.

                      Drawing seams using types

                      One of the primary attributes of unit tests in object-oriented languages, especially statically-typed ones, is the concept of “seams” within a codebase. These are internal boundaries between components of a system. Some boundaries are obvious—interactions with a database, manipulation of the file system, and performing I/O over the network, to name a few examples—but others are more subtle. Especially in larger codebases, it can be helpful to isolate two related but distinct pieces of functionality as much as possible, which makes them easier to reason about, even if they’re actually part of the same codebase.

                      In OO languages, these seams are often marked using interfaces, whether explicitly (in the case of static languages) or implicitly (in the case of dynamic ones). By programming to an interface, it’s possible to create “fake” implementations of that interface for use in unit tests, effectively making it possible to stub out code that isn’t directly relevant to the code being tested.

                      In Haskell, representing these seams is a lot less obvious. Consider a fairly trivial function that reverses a file’s contents on the file system:

                      reverseFile :: FilePath -> IO ()
                      +reverseFile path = do
                      +  contents <- readFile path
                      +  writeFile path (reverse contents)

                      This function is impossible to test without testing against a real file system. It simply performs I/O directly, and there’s no way to “mock out” the file system for testing purposes. Now, admittedly, this function is so trivial that a unit test might not seem worth the cost, but consider a slightly more complicated function that interacts with a database:

                      renderUserProfile :: Id User -> IO HTML
                      +renderUserProfile userId = do
                      +  user <- fetchUser userId
                      +  posts <- fetchRecentPosts userId
                      +
                      +  return $ div
                      +    [ h1 (userName user <> "’s Profile")
                      +    , h2 "Recent Posts"
                      +    , ul (map (li . postTitle) posts)
                      +    ]

                      It might now be a bit more clear that it could be useful to test the above function without running a real database and doing all the necessary context setup before each test case. Indeed, it would be nice if a test could just provide stubbed implementations for fetchUser and fetchRecentPosts, then make assertions about the output.

                      One way to solve this problem is to pass the results of those two functions to renderUserProfile as arguments, turning it into a pure function that could be easily tested. This becomes obnoxious for functions of even just slightly more complexity, though (it is not unreasonable to imagine needing a handful of different queries to render a user’s profile page), and it requires significantly restructuring code simply because the tests need it.

                      The above code is not only difficult to test, however—it has another problem, too. Specifically, both functions return IO values, which means they can effectively do anything. Haskell has a very strong type system for typing terms, but it doesn’t provide any guarantees about effects beyond a simple yes/no answer about function purity. Even though the renderUserProfile function should really only need to interact with the database, it could theoretically delete files, send emails, make HTTP requests, or do any number of other things.

                      Fortunately, it’s possible to solve both problems—a lack of testability and a lack of type safety—using the same general technique. This approach is reminiscent of the interface-based seams of object-oriented languages, but unlike most object-oriented approaches, it provides additional type safety guarantees without the need to explicitly modify the code to support some kind of dependency injection.

                      Making implicit interfaces explicit

                      Statically typed, object-oriented languages provide interfaces as a language construct to encode certain kinds of contracts into the type system, and Haskell has something similar. Typeclasses are, in many ways, an analog to OO interfaces, and they can be used in a similar way. In the above case, let’s write down interfaces that the reverseFile and renderUserProfile functions can use:

                      class Monad m => MonadFS m where
                      +  readFile :: FilePath -> m String
                      +  writeFile :: FilePath -> String -> m ()
                      +
                      +class Monad m => MonadDB m where
                      +  fetchUser :: Id User -> m User
                      +  fetchRecentPosts :: Id User -> m [Post]

                      The really nice thing about these interfaces is that our function implementations don’t have to change at all to take advantage of them. In fact, all we have to change is their types:

                      reverseFile :: MonadFS m => FilePath -> m ()
                      +reverseFile path = do
                      +  contents <- readFile path
                      +  writeFile path (reverse contents)
                      +
                      +renderUserProfile :: MonadDB m => Id User -> m HTML
                      +renderUserProfile userId = do
                      +  user <- fetchUser userId
                      +  posts <- fetchRecentPosts userId
                      +
                      +  return $ div
                      +    [ h1 (userName user <> "’s Profile")
                      +    , h2 "Recent Posts"
                      +    , ul (map (li . postTitle) posts)
                      +    ]

                      This is pretty neat, since we haven’t had to alter our code at all, but we’ve managed to completely decouple ourselves from IO. This has the direct effect of both making our code more abstract (we no longer rely on the “real” file system or a “real” database, which makes our code easier to test) and restricting what our functions can do (just from looking at the type signatures, we know what side-effects they can perform).

                      Of course, since we’re now coding against an interface, our code doesn’t actually do much of anything. If we want to actually use the functions we’ve written, we’ll have to define instances of MonadFS and MonadDB. When actually running our code, we’ll probably still use IO (or some monad transformer stack with IO at the bottom), so we can define trivial instances for that existing use case:

                      instance MonadFS IO where
                      +  readFile = Prelude.readFile
                      +  writeFile = Prelude.writeFile
                      +
                      +instance MonadDB IO where
                      +  fetchUser = SQL.fetchUser
                      +  fetchRecentPosts = SQL.fetchRecentPosts

                      Even if we go no further, this is already incredibly useful. By restricting the sorts of effects our functions can perform at the type level, it becomes a lot easier to see which code is interacting with what. This can be invaluable when working in a part of a moderately large codebase that you are unfamiliar with. Even if the only instance of these typeclasses is IO, the benefits are immediately apparent.

                      Of course, this blog post is about testing, so we’re going to go further and take advantage of these seams we’ve now drawn. The question is: how?

                      Testing with typeclasses: an initial attempt

                      Given that we now have functions depending on an interface instead of IO, we can create separate instances of our typeclasses for use in tests. Let’s start with the renderUserProfile function. We’ll create a simple wrapper around the Identity type, since we don’t actually care much about the “effects” of our MonadDB methods:

                      import Data.Functor.Identity
                      +
                      +newtype TestM a = TestM (Identity a)
                      +  deriving (Functor, Applicative, Monad)
                      +
                      +unTestM :: TestM a -> a
                      +unTestM (TestM (Identity x)) = x

                      Now, we’ll create a trivial instance of MonadDB for TestM:

                      instance MonadDB TestM where
                      +  fetchUser _ = return User { userName = "Alyssa" }
                      +  fetchRecentPosts _ = return
                      +    [ Post { postTitle = "Metacircular Evaluator" } ]

                      With this instance, it’s now possible to write a simple unit test of the renderUserProfile function that doesn’t need a real database running at all:

                      spec = describe "renderUserProfile" $ do
                      +  it "shows the user’s name" $ do
                      +    let result = unTestM (renderUserProfile (intToId 1234))
                      +    result `shouldContainElement` h1 "Alyssa’s Profile"
                      +
                      +  it "shows a list of the user’s posts" $ do
                      +    let result = unTestM (renderUserProfile (intToId 1234))
                      +    result `shouldContainElement` ul [ li "Metacircular Evaluator" ]

                      This is pretty nice, and running the above tests reveals a nice property of these kinds of isolated test cases: the test suite runs really, really fast. Communicating with a database, even in extremely simple ways, takes a measurable amount of time, especially with dozens of tests. In contrast, even with hundreds of tests, our unit test suite runs in less than a tenth of a second.

                      This all seems to be successful, so let’s try and apply the same testing technique to reverseFile.

                      Testing side-effectful code

                      Looking at the type signature for reverseFile, we have a small problem:

                      reverseFile :: MonadFS m => FilePath -> m ()

                      Specifically, the return type is (). Making any assertions against the result of this function would be completely worthless, given that it’s guaranteed to be the same exact thing each time. Instead, reverseFile is inherently side-effectful, so we want to be able to test that it properly interacts with the file system in the correct way.

                      In order to do this, a simple wrapper around Identity won’t be enough, but we can replace it with something more powerful: Writer. Specifically, we can use a writer monad to “log” what gets called in order to test side-effects. We’ll start by creating a new TestM type, just like last time:

                      newtype TestM a = TestM (Writer [String] a)
                      +  deriving (Functor, Applicative, Monad, MonadWriter [String])
                      +
                      +logTestM :: TestM a -> [String]
                      +logTestM (TestM w) = execWriter w

                      Using this slightly more powerful type, we can write a useful instance of MonadFS that will track the argument given to writeFile:

                      instance MonadFS TestM where
                      +  readFile _ = return "hello"
                      +  writeFile _ contents = tell [contents]

                      Again, the instance is quite simple, but it now enables us to write a straightforward unit test for reverseFile:

                      spec = describe "reverseFile" $
                      +  it "reverses a file’s contents on the filesystem" $ do
                      +    let calls = logTestM (reverseFile "foo.txt")
                      +    calls `shouldBe` ["olleh"]

                      Again, quite simple to both implement and use, and the test itself is blindingly fast. There’s another problem, though, which is that we have technically left part of reverseFile untested: we’ve completely ignored the path argument.

                      In this contrived example, it may seem silly to test something so trivial, but in real code, it’s quite possible that one would care very much about testing multiple different aspects about a single function. When testing renderUserProfile, this was not hard, since we could reuse the same TestM type and MonadDB instance for both test cases, but in the reverseFile example, we’ve ignored the path entirely.

                      We could adjust our MonadFS instance to also track the path provided to each method, but this has a few problems. First, it means every test case would depend on all the various properties we are testing, which would mean updating every test case when we add a new one. It would also be simply impossible if we needed to track multiple types—in this particular case, it turns out that String and FilePath are actually the same type, but in practice, there may be a handful of disparate, incompatible types.

                      Both of the above issues could be fixed by creating a sum type and manually filtering out the relevant elements in each test case, but a much more intuitive approach would be to simply have a separate instance for each case. Unfortunately, in Haskell, creating a new instance means creating an entirely new type. To illustrate how much duplication that would entail, we could create the following type and instance for testing proper propagation of the path argument:

                      newtype TestM' a = TestM' (Writer [FilePath] a)
                      +  deriving (Functor, Applicative, Monad, MonadWriter [FilePath])
                      +
                      +logTestM' :: TestM' a -> [FilePath]
                      +logTestM' (TestM' w) = execWriter w
                      +
                      +instance MonadFS TestM' where
                      +  readFile path = tell [path] >> return ""
                      +  writeFile path _ = tell [path]

                      Now it’s possible to add an extra test case that asserts that the proper path is provided to the two filesystem functions:

                      spec = describe "reverseFile" $ do
                      +  it "reverses a file’s contents on the filesystem" $ do
                      +    let calls = logTestM (reverseFile "foo.txt")
                      +    calls `shouldBe` ["olleh"]
                      +
                      +  it "operates on the file at the provided path" $ do
                      +    let paths = logTestM' (reverseFile "foo.txt")
                      +    paths `shouldBe` ["foo.txt", "foo.txt"]

                      This works, but it’s ultimately unacceptably complicated. Our test harness code is now significantly larger than the actual tests themselves, and the amount of boilerplate is frustrating. Verbose test suites are especially bad, since forcing programmers to jump through hoops just to implement a single test reduces the likelihood that people will actually write good tests, if they write tests at all. In contrast, if writing tests is easy, then people will naturally write more of them.

                      The above strategy to writing tests is not good enough, but it does reveal a particular problem: in Haskell, typeclass instances are not first-class values that can be manipulated and abstracted over, they are static constructs that can only be managed by the compiler, and users do not have a direct way to modify them. With some cleverness, however, we can actually create an approximation of first-class typeclass dictionaries, which will allow us to dramatically simplify the above testing mechanism.

                      Creating first-class typeclass instances

                      In order to provide an easy way to construct instances, we need a way to represent instances as ordinary Haskell values. This is not terribly difficult, given that instances are conceptually just records containing a collection of functions. For example, we could create a datatype that represents an instance of the MonadFS typeclass:

                      data MonadFSInst m = MonadFSInst
                      +  { _readFile :: FilePath -> m String
                      +  , _writeFile :: FilePath -> String -> m ()
                      +  }

                      To avoid namespace clashes with the actual method identifiers, the record fields are prefixed with an underscore, but otherwise, the translation is remarkably straightforward. Using this record type, we can easily create values that represent the two instances we defined above:

                      contentInst :: MonadWriter [String] m => MonadFSInst m
                      +contentInst = MonadFSInst
                      +  { _readFile = \_ -> return "hello"
                      +  , _writeFile = \_ contents -> tell [contents]
                      +  }
                      +
                      +pathInst :: MonadWriter [FilePath] m => MonadFSInst m
                      +pathInst = MonadFSInst
                      +  { _readFile = \path -> tell [path] >> return ""
                      +  , _writeFile = \path _ -> tell [path]
                      +  }

                      These two values represent two different implementations of MonadFS, but since they’re ordinary Haskell values, they can be manipulated and even extended like any other records. This can be extremely useful, since it makes it possible to create a sort of “base” instance, then have individual test cases override individual pieces of functionality piecemeal.

                      Of course, although we’ve written these two instances, we have no way to actually use them. After all, Haskell does not provide a way to explicitly provide typeclass dictionaries. Fortunately, we can create a sort of “proxy” type that will use a reader to thread the dictionary around explicitly, and the instance can defer to the dictionary’s implementation.

                      Creating an instance proxy

                      To represent our proxy type, we’ll use a combination of a Writer and a ReaderT; the former to implement the logging used by instances, and the latter to actually thread around the dictionary. Our type will look like this:

                      newtype TestM log a =
                      +    TestM (ReaderT (MonadFSInst (TestM log)) (Writer log) a)
                      +  deriving ( Functor, Applicative, Monad
                      +           , MonadReader (MonadFSInst (TestM log))
                      +           , MonadWriter log
                      +           )
                      +
                      +logTestM :: MonadFSInst (TestM log) -> TestM log a -> log
                      +logTestM inst (TestM m) = execWriter (runReaderT m inst)

                      This might look rather complicated, and it is, but let’s break down exactly what it’s doing.

                      1. The TestM type includes two type parameters. The first is the type of value that will be logged (hence the name log), which corresponds to the argument to Writer from previous incarnations of TestM. Unlike those types, though, we want this version to work with any Monoid, so we’ll make it a type parameter. The second parameter is simply the type of the current monadic value, as before.

                      2. The type itself is defined as a wrapper around a small monad transformer stack, the first of which is ReaderT. The state threaded around by the reader is, in this case, the instance dictionary, which is MonadFSInst.

                        However, recall that MonadFSInst accepts a type variable—the type of a monad itself—so we must provide TestM log as an argument to MonadFSInst. This slight bit of indirection allows us to tie the knot between the mutually dependent instances and proxy type.

                      3. The base monad in the transformer stack is Writer, which is used to actually implement the logging functionality, just like in prior cases. The only difference now is that the log type parameter now determines what the writer actually produces.

                      4. Finally, as before, we use GeneralizedNewtypeDeriving to derive all the relevant mtl classes, adding the somewhat wordy MonadReader constraint to the list.

                      Using this single type, we can now implement a MonadFS instance that defers to the dictionary carried around within TestM’s reader state:

                      instance Monoid log => MonadFS (TestM log) where
                      +  readFile path = do
                      +    f <- asks _readFile
                      +    f path
                      +  writeFile path contents = do
                      +    f <- asks _writeFile
                      +    f path contents

                      This may seem somewhat boilerplate-y, and it is to some extent, but the important consideration is that this boilerplate only needs to be written once. With this in place, it’s now possible to write an arbitrary number of first-class instances that use the above mechanism without extending the mechanism at all.

                      To see what actually using this code would look like, let’s update the reverseFile tests to use the new TestM implementation, as well as the contentInst and pathInst dictionaries from earlier:

                      spec = describe "reverseFile" $ do
                      +  it "reverses a file’s contents on the filesystem" $ do
                      +    let calls = logTestM contentInst (reverseFile "foo.txt")
                      +    calls `shouldBe` ["olleh"]
                      +
                      +  it "operates on the file at the provided path" $ do
                      +    let paths = logTestM pathInst (reverseFile "foo.txt")
                      +    paths `shouldBe` ["foo.txt", "foo.txt"]

                      We can do a little bit better, though. Really, the definitions of contentInst and pathInst are specific to each test case. With ordinary typeclass instances, we cannot scope them to any particular block, but since MonadFSInst is just an ordinary Haskell datatype, we can manipulate them just like any other Haskell values. Therefore, we can just inline those instances’ definitions into the test cases themselves to keep them closer to the actual tests.

                      spec = describe "reverseFile" $ do
                      +  it "reverses a file’s contents on the filesystem" $ do
                      +    let contentInst = MonadFSInst
                      +          { _readFile = \_ -> return "hello"
                      +          , _writeFile = \_ contents -> tell [contents]
                      +          }
                      +    let calls = logTestM contentInst (reverseFile "foo.txt")
                      +    calls `shouldBe` ["olleh"]
                      +
                      +  it "operates on the file at the provided path" $ do
                      +    let pathInst = MonadFSInst
                      +          { _readFile = \path -> tell [path] >> return ""
                      +          , _writeFile = \path _ -> tell [path]
                      +          }
                      +    let paths = logTestM pathInst (reverseFile "foo.txt")
                      +    paths `shouldBe` ["foo.txt", "foo.txt"]

                      This is pretty good. We’re now able to create inline instances of our MonadFS typeclass, which allows us to write extremely concise unit tests using ordinary Haskell typeclasses as system seams. We’ve managed to cut down on the boilerplate considerably, though we still have a couple problems. For one, this example only uses a single typeclass containing only two methods. A real MonadFS typeclass would likely have at least a dozen methods for performing various filesystem operations, and writing out the instance dictionaries for every single method, even the ones that aren’t used within the code under test, would be pretty frustratingly verbose.

                      This problem is solvable, though. Since instances are just ordinary Haskell records, we can create a “base” instance that just throws an exception whenever the method is called:

                      baseInst :: MonadFSInst m
                      +baseInst = MonadFSInst
                      +  { _readFile = error "unimplemented instance method ‘_readFile’"
                      +  , _writeFile = error "unimplemented instance method ‘_writeFile’"
                      +  }

                      Then code that only uses readFile could only override that particular method, for example:

                      let myInst = baseInst { _readFile = ... }

                      Normally, of course, this would be a terrible idea. However, since this is all just test code, it can be extremely useful in quickly figuring out what methods need to be stubbed out for a particular test case. Since all the code actually gets run at test time, attempts to use unimplemented instance methods will immediately raise an error, informing the programmer which methods need to be implemented to make the test pass. This can also help to significantly cut down on the amount of effort it takes to implement each test.

                      Another problem is that our approach is specialized exclusively to MonadFS. What about functions that use both MonadFS and MonadDB, for example? Fortunately, that is not hard to solve, either. We can adapt the MonadFSInst type to include fields for all of the typeclasses relevant to our system, turning it into a generic test fixture of sorts:

                      data FixtureInst m = FixtureInst
                      +  { -- MonadFS
                      +    _readFile :: FilePath -> m String
                      +  , _writeFile :: FilePath -> String -> m ()
                      +
                      +    -- MonadDB
                      +  , _fetchUser :: Id User -> m User
                      +  , _fetchRecentPosts :: Id User -> m [Post]
                      +  }

                      Updating TestM to use FixtureInst instead of MonadFSInst is trivial, and all the rest of the infrastructure still works. However, this means that every time a new typeclass is added, three things need to be updated:

                      1. Its methods need to be added to the FixtureInst record.

                      2. Those methods need to be given error-raising defaults in the baseInst value.

                      3. An actual instance of the typeclass needs to be written for TestM that defers to the FixtureInst value.

                      Furthermore, most of this manual manipulation of methods is required every time a particular typeclass changes, whether that means adding a method, removing a method, renaming a method, or changing a method’s type. This is especially frustrating given that all this code is really just mechanical boilerplate that could all be derived by the set of typeclasses being tested.

                      That last point is especially important: aside from the instances themselves, every piece of boilerplate above is obviously possible to generate from existing types alone. With that piece of information in mind, we can do even better: we can use Template Haskell.

                      Removing the boilerplate using test-fixture

                      The above code was not only rather boilerplate-heavy, it was pretty complicated. Fortunately, you don’t actually have to write it. Enter the library test-fixture:

                      import Control.Monad.TestFixture
                      +import Control.Monad.TestFixture.TH
                      +
                      +mkFixture "FixtureInst" [''MonadFS, ''MonadDB]
                      +
                      +spec = describe "reverseFile" $ do
                      +  it "reverses a file’s contents on the filesystem" $ do
                      +    let contentInst = def
                      +          { _readFile = \_ -> return "hello"
                      +          , _writeFile = \_ contents -> log contents
                      +          }
                      +    let calls = logTestFixture (reverseFile "foo.txt") contentInst
                      +    calls `shouldBe` ["olleh"]
                      +
                      +  it "operates on the file at the provided path" $ do
                      +    let pathInst = def
                      +          { _readFile = \path -> log path >> return ""
                      +          , _writeFile = \path _ -> log path
                      +          }
                      +    let paths = logTestFixture (reverseFile "foo.txt") pathInst
                      +    paths `shouldBe` ["foo.txt", "foo.txt"]

                      That’s it. The above code automatically generates everything you need to write fast, simple, deterministic unit tests in Haskell. The mkFixture function is a Template Haskell macro that expands into a definition quite similar to the FixtureInst type we wrote by hand, but since it’s automatically generated from the typeclass definitions, it never needs to be updated.

                      The logTestFixture function replaces the logTestM function we wrote by hand, but it works exactly the same. The Control.Monad.TestFixture library also exports a log function that is a synonym for tell . singleton, but using tell directly still works if you prefer.

                      The mkFixture function also generates a Default instance, which replaces the baseInst value defined earlier. It functions the same way, though, producing useful error messages that refer to the names of unimplemented typeclass methods that have not been stubbed out.

                      This blog post is not a test-fixture tutorial—indeed, it is much more complicated than a test-fixture tutorial would be, since it covers what the library is really doing under the hood—but if you’re interested, I would highly recommend you take a look at the test-fixture documentation on Hackage.

                      Conclusion, credits, and similar techniques

                      This blog post came about as the result of a need my coworkers and I found when writing Haskell code; we wanted a way to write unit tests quickly and easily, but we didn’t find much advice from the rest of the Haskell ecosystem. The test-fixture library is the result of that exploratory work, and we currently use it to test a significant portion of our Haskell code.

                      It would be extremely unfair to suggest that I was the inventor of this technique or the inventor of the library. Two of my coworkers, Joe Vargas and Greg Wiley, came up with the general approach and wrote Control.Monad.TestFixture, and I simply wrote the Template Haskell macro to eliminate the boilerplate. With that in mind, I think I can say with some fairness that I think this technique is a joy to use when unit testing is a desirable goal, and I would definitely recommend it if you are interested in doing isolated testing in Haskell.

                      The general technique of using typeclasses to emulate effects was in part inspired by the well-known mtl library. An alternate approach to writing unit-testable Haskell code is using free monads, but overall, I prefer this approach over free monads because the typeclass constraints add type safety in ways that free monads do not (at least not without additional boilerplate), and this approach also lends itself well to static analysis-based boilerplate reduction techniques. It has its own tradeoffs, though, so if you’ve had success with free monads, then I certainly make no claim this is a superior approach, just one that I’ve personally found pleasant.

                      As a final note, if you do check out test-fixture, feel free to leave feedback by opening issues on the GitHub issue tracker—even things like confusing documentation are worth a bug report.

                        \ No newline at end of file diff --git a/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/index.html b/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/index.html new file mode 100644 index 0000000..a30abec --- /dev/null +++ b/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/index.html @@ -0,0 +1,26 @@ +Rascal: a Haskell with more parentheses

                        Rascal: a Haskell with more parentheses

                        Note: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in the followup blog post.

                        “Hey! You got your Haskell in my Racket!”

                        “No, you got your Racket in my Haskell!”

                        Welcome to the Rascal programming language.

                        Why Rascal?

                        Why yet another programming language? Anyone who knows me knows that I already have two programming languages that I really like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.

                        Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that is something I want to write at some point). What you should understand, though, is that to me, Haskell is pretty close to what I want in a programming language.

                        At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and even then the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.

                        Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one fact that I want to bring up, which is that Haskell does not provide any mechanism for adding syntactic abstractions to the language. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.

                        A programmable programming language: theory and practice

                        I feel confident in saying that Racket has the most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a programmable programming language, a language for building languages.

                        This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way you want it to look, poking and prodding at a pliable substrate. The sheer ease of it all is impossible for me to convey in words, so you will have to see it for yourself.

                        All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, I gave a talk on some of Racket’s linguistic capabilities last year called Languages in an Afternoon. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some blog posts on this very blog that demonstrate what Racket can do.

                        The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the racket/match module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually extensible, so users can add their own pattern-matching forms that cooperate with match.

                        This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of minutes, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.

                        Fusing Haskell and Racket

                        So, let’s assume that we do want Haskell’s strong type system and that we also want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:

                        1. First of all, it’s easy to implement. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.

                        2. Unfortunately, there’s a huge problem with this approach: type information is not available at macroexpansion time. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.

                        For me, the second option is unacceptable. I am not satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.

                        There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).

                        Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called Type Systems as Macros, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.

                        The result is Rascal, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.

                        A first peek at Rascal

                        Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually use it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.

                        First, let me say this up front: Rascal is probably a lot closer to Haskell than Racket. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.

                        Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:1

                        (def+ map-every-other : (forall [a] {{a -> a} -> (List a) -> (List a)})
                        +  [_ nil            -> nil]
                        +  [_ {x :: nil}     -> {x :: nil}]
                        +  [f {x :: y :: ys} -> {x :: (f y) :: (map-every-other f ys)}])
                        +

                        This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, typeclasses. Yes, with Rascal you can have your monads in all their statically dispatched glory:

                        (data (Maybe a)
                        +  (just a)
                        +  nothing)
                        +
                        +(instance (Monad Maybe)
                        +  [join (case-lambda
                        +          [(just (just x)) (just x)]
                        +          [_               nothing])])
                        +

                        So far, though, this really is just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.

                        Core forms can be implemented as derived concepts

                        Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros data and case, in an entirely separate module, which can be imported just like any other library.

                        The main rascal language provides ADTs by default, of course, but it would be perfectly possible to produce a rascal/kernel language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.

                        Simple syntactic transformations are, of course, trivially defined as macros. Haskell do notation is defined as an eleven-line macro in rascal/monad, and GHC’s useful LambdaCase extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.

                        While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like GeneralizedNewtypeDeriving and other generic deriving mechanisms like GHC.Generics, DeriveGeneric, and DeriveAnyClass.

                        The language is not enough

                        No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do everything, and it’s impossible to do everything well.

                        Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like servant let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.

                        Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:

                        type UserAPI = "users" :> Get '[JSON] [User]
                        +          :<|> "users" :> ReqBody '[JSON] User :> Post '[JSON] User
                        +          :<|> "users" :> Capture "userid" Integer
                        +                       :> Get '[JSON] User
                        +          :<|> "users" :> Capture "userid" Integer
                        +                       :> ReqBody '[JSON] User
                        +                       :> Put '[JSON] User
                        +

                        The above code is remarkably readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:

                        (define-api User-API
                        +  #:content-types [JSON]
                        +  [GET  "users"                    => (List User)]
                        +  [POST "users"                    => User -> User]
                        +  [GET  "users" [userid : Integer] => User]
                        +  [PUT  "users" [userid : Integer] => User -> User])
                        +

                        This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).

                        In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a runtime interpreter for your language! Macros, on the other hand, are compiled, and you get ability to compile your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.

                        Rascal is embryonic

                        I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to completely different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.

                        That said, Rascal doesn’t really exist yet. Yes, there is a GitHub repository, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do anything interesting with it, aside from some tiny toy programs.

                        As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.

                        Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank Mark P Jones for his wonderful resource Typing Haskell in Haskell, without which getting to where I am now would likely have been impossible. I also want to thank Stephen Diehl for his wonderful Write You a Haskell series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.

                        Even with these wonderful resources, I’ve come to the realization that I probably can’t do all of this on my own. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!

                        In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of Type Systems as Macros for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.

                        1. Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet.

                        \ No newline at end of file diff --git a/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/index.html b/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/index.html new file mode 100644 index 0000000..678ebca --- /dev/null +++ b/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/index.html @@ -0,0 +1 @@ +Rascal is now Hackett, plus some answers to questions

                        Rascal is now Hackett, plus some answers to questions

                        Since I published my blog post introducing Rascal, I’ve gotten some amazing feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that Rascal is a language that already exists. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, Rascal is now known as Hackett.

                        With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.

                        What’s in a name?

                        First, a little trivia.

                        I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.

                        Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the Genesis progressive rock guitarist, Steve Hackett, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.

                        Perhaps not the most interesting thing in this blog post, but there it is.

                        Why Racket? Why not Haskell?

                        One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: Racket is actually two things, a programming language and a programming language platform. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.

                        Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than #lang racket, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that #lang racket was ever the more “dominant” language, aside from the name.

                        For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, Languages in an Afternoon, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.

                        By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:

                        1. I get a JIT compiler for my code, and I don’t have to implement a compiler myself.

                        2. I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.

                        3. I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.

                        4. The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).

                        5. If you don’t want to use DrRacket, you can use the racket-mode major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.

                        Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.

                        In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the Type Systems as Macros paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander alone in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.

                        Actually running Hackett code

                        Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.

                        This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.

                        There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.

                        How do Template Haskell quasiquoters compete with macros?

                        Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.

                        S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has syntax/parse), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider persistent’s quasiquoters: they look sort of like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.

                        Additionally, s-expression macros compose, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s match, for example, is an expression, and it contains expressions, so match can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.

                        Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.

                        Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing which identifiers are uses and which identifiers are bindings, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens. One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be abstractions. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.

                        How can I help?

                        Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, which you can sign up for here).

                        If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.

                          \ No newline at end of file diff --git a/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/index.html b/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/index.html new file mode 100644 index 0000000..3920e1b --- /dev/null +++ b/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/index.html @@ -0,0 +1,176 @@ +Lifts for free: making mtl typeclasses derivable

                          Lifts for free: making mtl typeclasses derivable

                          ⦿ haskell

                          Perhaps the most important abstraction a Haskell programmer must understand to effectively write modern Haskell code, beyond the level of the monad, is the monad transformer, a way to compose monads together in a limited fashion. One frustrating downside to monad transformers is a proliferation of lifts, which explicitly indicate which monad in a transformer “stack” a particular computation should run in. Fortunately, the venerable mtl provides typeclasses that make this lifting mostly automatic, using typeclass machinery to insert lift where appropriate.

                          Less fortunately, the mtl approach does not actually eliminate lift entirely, it simply moves it from use sites to instances. This requires a small zoo of extraordinarily boilerplate-y instances, most of which simply implement each typeclass method using lift. While we cannot eliminate the instances entirely without somewhat dangerous techniques like overlapping instances, we can automatically derive them using features of modern GHC, eliminating the truly unnecessary boilerplate.

                          The problem with mtl-style typeclasses

                          To understand what problem it is exactly that we’re trying to solve, we first need to take a look at an actual mtl-style typeclass. I am going to start with an mtl-style typeclass, rather than an actual typeclass in the mtl, due to slight complications with mtl’s actual typeclasses that we’ll get into later. Instead, let’s start with a somewhat boring typeclass, which we’ll call MonadExit:

                          import System.Exit (ExitCode)
                          +
                          +class Monad m => MonadExit m where
                          +  exitWith :: ExitCode -> m ()

                          This is a simple typeclass that abstracts over the concept of early exit, given an exit code. The most obvious implementation of this typeclass is over IO, which will actually exit the program:

                          import qualified System.Exit as IO (exitWith)
                          +
                          +instance MonadExit IO where
                          +  exitWith = IO.exitWith

                          One of the cool things about these typeclasses, though, is that we don’t have to have just one implementation. We could also write a pure implementation of MonadExit, which would simply short-circuit the current computation and return the ExitCode:

                          instance MonadExit (Either ExitCode) where
                          +  exitWith = Left

                          Instead of simply having an instance on a concrete monad, though, we probably want to be able to use this in a larger monad stack, so we can define an ExitT monad transformer that can be inserted into any monad transformer stack:

                          {-# LANGUAGE GeneralizedNewtypeDeriving #-}
                          +
                          +import Control.Monad.Except (ExceptT, runExceptT, throwError)
                          +import Control.Monad.Trans (MonadTrans)
                          +
                          +newtype ExitT m a = ExitT (ExceptT ExitCode m a)
                          +  deriving (Functor, Applicative, Monad, MonadTrans)
                          +
                          +runExitT :: ExitT m a -> m (Either ExitCode a)
                          +runExitT (ExitT x) = runExceptT x
                          +
                          +instance Monad m => MonadExit (ExitT m) where
                          +  exitWith = ExitT . throwError

                          With this in place, we can write actual programs using our ExitT monad transformer:

                          ghci> runExitT $ do
                          +        lift $ putStrLn "hello"
                          +        exitWith (ExitFailure 1)
                          +        lift $ putStrLn "world"
                          +hello
                          +Left (ExitFailure 1)

                          This is pretty cool! Unfortunately, experienced readers will see the rather large problem with what we have so far. Specifically, it won’t actually work if we try and wrap ExitT in another monad transformer:

                          ghci> logIn password = runExitT $ flip runReaderT password $ do
                          +        password <- ask
                          +        unless (password == "password1234") $ -- super secure password
                          +          exitWith (ExitFailure 1)
                          +        return "access granted"
                          +
                          +ghci> logIn "not the right password"
                          +<interactive>: error:
                          +     No instance for (MonadExit (ReaderT [Char] (ExitT m0)))
                          +        arising from a use of it
                          +     In a stmt of an interactive GHCi command: print it

                          The error message is relatively self-explanatory if you are familiar with mtl error messages: there is no MonadExit instance for ReaderT. This makes sense, since we only defined a MonadExit instance for ExitT, nothing else. Fortunately, the instance for ReaderT is completely trivial, since we just need to use lift to delegate to the next monad in the stack:

                          instance MonadExit m => MonadExit (ReaderT r m) where
                          +  exitWith = lift . exitWith

                          Now that the delegating instance is set up, we can actually use our logIn function:

                          ghci> logIn "not the right password"
                          +Left (ExitFailure 1)
                          +ghci> logIn "password1234"
                          +Right "access granted"

                          An embarrassment of instances

                          We’ve managed to make our program work properly now, but we’ve still only defined the delegating instance for ReaderT. What if someone wants to use ExitT with WriterT? Or StateT? Or any of ExceptT, RWST, or ContT? Well, we have to define instances for each and every one of them, and as it turns out, the instances are all identical!

                          instance (MonadExit m, Monoid w) => MonadExit (WriterT w m) where
                          +  exitWith = lift . exitWith
                          +
                          +instance MonadExit m => MonadExit (StateT s m) where
                          +  exitWith = lift . exitWith
                          +
                          +instance (MonadExit m, Monoid w) => MonadExit (RWST r w s m) where
                          +  exitWith = lift . exitWith
                          +
                          +instance MonadExit m => MonadExit (ExceptT e m) where
                          +  exitWith = lift . exitWith
                          +
                          +instance MonadExit m => MonadExit (ContT r m) where
                          +  exitWith = lift . exitWith

                          This is bad enough on its own, but this is actually the simplest case: a typeclass with a single method which is trivially lifted through any other monad transformer. Another thing we’ve glossed over is actually defining all the delegating instances for the other mtl typeclasses on ExitT itself. Fortunately, we can derive these ones with GeneralizedNewtypeDeriving, since ExceptT has already done most of the work for us:

                          newtype ExitT m a = ExitT (ExceptT ExitCode m a)
                          +  deriving ( Functor, Applicative, Monad, MonadIO -- base
                          +           , MonadBase IO -- transformers-base
                          +           , MonadTrans, MonadReader r, MonadWriter w, MonadState s -- mtl
                          +           , MonadThrow, MonadCatch, MonadMask -- exceptions
                          +           , MonadTransControl, MonadBaseControl IO -- monad-control
                          +           )

                          Unfortunately, we have to write the MonadError instance manually if we want it, since we don’t want to pick up the instance from ExceptT, but rather wish to defer to the underlying monad. This means writing some truly horrid delegation code:

                          instance MonadError e m => MonadError e (ExitT m) where
                          +  throwError = lift . throwError
                          +
                          +  catchError (ExitT x) f = ExitT . ExceptT $ catchError (runExceptT x) $ \e ->
                          +    let (ExitT x') = f e in runExceptT x'

                          (Notably, this is so awful because catchError is more complex than the simple exitWith method we’ve studied so far, which is why we’re starting with a simpler typeclass. We’ll get more into this later, as promised.)

                          This huge number of instances is sometimes referred to as the “n2 instances” problem, since it requires every monad transformer have an instance of every single mtl-style typeclass. Fortunately, in practice, this proliferation is often less horrible than it might seem, mostly because deriving helps a lot. However, remember that if ExitT weren’t a simple wrapper around an existing monad transformer, we wouldn’t be able to derive the instances at all! Instead, we’d have to write them all out by hand, just like we did with all the MonadExit instances.

                          It’s a shame that these typeclass instances can’t be derived in a more general way, allowing derivation for arbitrary monad transformers instead of simply requiring the newtype deriving machinery. As it turns out, with clever use of modern GHC features, we actually can. It’s not even all that hard.

                          Default instances with default signatures

                          It’s not hard to see that our MonadExit instances are all exactly the same: just lift . exitWith. Why is that, though? Well, every instance is an instance on a monad transformer over a monad that is already an instance of MonadExit. In fact, we can express this in a type signature, and we can extract lift . exitWith into a separate function:

                          defaultExitWith :: (MonadTrans t, MonadExit m) => ExitCode -> t m ()
                          +defaultExitWith = lift . exitWith

                          However, writing defaultExitWith really isn’t any easier than writing lift . exitWith, so this deduplication doesn’t really buy us anything. However, it does indicate that we could write a default implementation of exitWith if we could require just a little bit more from the implementing type. With GHC’s DefaultSignatures extension, we can do precisely that.

                          The idea is that we can write a separate type signature for a default implementation of exitWith, which can be more specific than the type signature for exitWith in general. This allows us to use our defaultExitWith implementation more or less directly:

                          {-# LANGUAGE DefaultSignatures #-}
                          +
                          +class Monad m => MonadExit m where
                          +  exitWith :: ExitCode -> m ()
                          +
                          +  default exitWith :: (MonadTrans t, MonadExit m1) => ExitCode -> t m1 ()
                          +  exitWith = lift . exitWith

                          We have to use m1 instead of m, since type variables in the instance head are always scoped, and the names would conflict. However, this creates another problem, since our specialized type signature replaces m with t m1, which won’t quite work (as GHC can’t automatically figure out they should be the same). Instead, we can use m in the type signature, then just add a type equality constraint ensuring that m and t m1 must be the same type:

                          class Monad m => MonadExit m where
                          +  exitWith :: ExitCode -> m ()
                          +
                          +  default exitWith :: (MonadTrans t, MonadExit m1, m ~ t m1) => ExitCode -> m ()
                          +  exitWith = lift . exitWith

                          Now we can write all of our simple instances without even needing to write a real implementation! All of the instance bodies can be empty:

                          instance MonadExit m => MonadExit (ReaderT r m)
                          +instance (MonadExit m, Monoid w) => MonadExit (WriterT w m)
                          +instance MonadExit m => MonadExit (StateT s m)
                          +instance (MonadExit m, Monoid w) => MonadExit (RWST r w s m)
                          +instance MonadExit m => MonadExit (ExceptT e m)
                          +instance MonadExit m => MonadExit (ContT r m)

                          While this doesn’t completely alleviate the pain of writing instances, it’s definitely an improvement over what we had before. With GHC 8.2’s new DerivingStrategies extension, it becomes especially beneficial when defining entirely new transformers that should also have ExitT instances, since they can be derived with DeriveAnyClass:

                          newtype ParserT m a = ParserT (Text -> m (Maybe (Text, a)))
                          +  deriving anyclass (MonadExit)

                          This is pretty wonderful.

                          Given that only MonadExit supports being derived in this way, we sadly still need to implement the other, more standard mtl-style typeclasses ourselves, like MonadIO, MonadBase, MonadReader, MonadWriter, etc. However, what if all of those classes provided the same convenient default signatures that our MonadExit does? If that were the case, then we could write something like this:

                          newtype ParserT m a = ParserT (Text -> m (Maybe (Text, a)))
                          +  deriving anyclass ( MonadIO, MonadBase b
                          +                    , MonadReader r, MonadWriter w, MonadState s
                          +                    , MonadThrow, MonadCatch, MonadMask
                          +                    , MonadExit
                          +                    )

                          Compared to having to write all those instances by hand, this would be a pretty enormous difference. Unfortunately, many of these typeclasses are not quite as simple as our MonadExit, and we’d have to be a bit more clever to make them derivable.

                          Making mtl’s classes derivable

                          Our MonadExit class was extremely simple, since it only had a single method with a particularly simple type signature. For reference, this was the type of our generic exitWith:

                          exitWith :: MonadExit m => ExitCode -> m ()

                          Let’s now turn our attention to MonadReader. At first blush, this typeclass should not be any trickier to implement than MonadExit, since the types of ask and reader are both quite simple:

                          ask :: MonadReader r m => m r
                          +reader :: MonadReader r m => (r -> a) -> m a

                          However, the type of the other method, local, throws a bit of a wrench in our plans. It has the following type signature:

                          local :: MonadReader r m => (r -> r) -> m a -> m a

                          Why is this so much more complicated? Well, the key is in the second argument, which has the type m a. That’s not something that can be simply lifted away! Try it yourself: try to write a MonadReader instance for some monad transformer. It’s not as easy as it looks!

                          We can illustrate the problem by creating our own version of MonadReader and implementing it for something like ExceptT ourselves. We can start with the trivial methods first:

                          class Monad m => MonadReader r m | m -> r where
                          +  ask :: m r
                          +  local :: (r -> r) -> m a -> m a
                          +  reader :: (r -> a) -> m a
                          +
                          +instance MonadReader r m => MonadReader r (ExceptT e m) where
                          +  ask = lift ask
                          +  reader = lift . reader

                          However, implementing local is harder. Let’s specialize the type signature to ExceptT to make it more clear why:

                          local :: MonadReader r m => (r -> r) -> ExceptT e m a -> ExceptT e m a

                          Our base monad, m, implements local, but we have to convert the first argument from ExceptT e m a into m (Either e a) first, run it through local in m, then wrap it back up in ExceptT:

                          instance MonadReader r m => MonadReader r (ExceptT e m) where
                          +  ask = lift ask
                          +  reader = lift . reader
                          +  local f x = ExceptT $ local f (runExceptT x)

                          This operation is actually a mapping operation of sorts, since we’re mapping local f over x. For that reason, this can be rewritten using the mapExceptT function provided from Control.Monad.Except:

                          instance MonadReader r m => MonadReader r (ExceptT e m) where
                          +  ask = lift ask
                          +  reader = lift . reader
                          +  local = mapExceptT . local

                          If you implement MonadReader instances for other transformers, like StateT and WriterT, you’ll find that the instances are exactly the same except for mapExceptT, which is replaced with mapStateT and mapWriterT, respectively. This is sort of obnoxious, given that we want to figure out how to create a generic version of local that works with any monad transformer, but this requires concrete information about which monad we’re in. Obviously, the power MonadTrans gives us is not enough to make this generic. Fortunately, there is a typeclass which does: MonadTransControl from the monad-control package.

                          Using MonadTransControl, we can write a generic mapT function that maps over an arbitrary monad transformer with a MonadTransControl instance:

                          mapT :: (Monad m, Monad (t m), MonadTransControl t)
                          +     => (m (StT t a) -> m (StT t b))
                          +     -> t m a
                          +     -> t m b
                          +mapT f x = liftWith (\run -> f (run x)) >>= restoreT . return

                          This type signature may look complicated (and, well, it is), but the idea is that the StT associated type family encapsulates the monadic state that t introduces. For example, for ExceptT, StT (ExceptT e) a is Either e a. For StateT, StT (StateT s) a is (a, s). Some transformers, like ReaderT, have no state, so StT (ReaderT r) a is just a.

                          I will not go into the precise mechanics of how MonadTransControl works in this blog post, but it doesn’t matter significantly; the point is that we can now use mapT to create a generic implementation of local for use with DefaultSignatures:

                          class Monad m => MonadReader r m | m -> r where
                          +  ask :: m r
                          +  default ask :: (MonadTrans t, MonadReader r m1, m ~ t m1) => m r
                          +  ask = lift ask
                          +
                          +  local :: (r -> r) -> m a -> m a
                          +  default local :: (MonadTransControl t, MonadReader r m1, m ~ t m1) => (r -> r) -> m a -> m a
                          +  local = mapT . local
                          +
                          +  reader :: (r -> a) -> m a
                          +  reader f = f <$> ask

                          Once more, we now get instances of our typeclass, in this case MonadReader, for free:

                          instance MonadReader r m => MonadReader r (ExceptT e m)
                          +instance (MonadReader r m, Monoid w) => MonadReader r (WriterT w m)
                          +instance MonadReader r m => MonadReader r (StateT s m)

                          It’s also worth noting that we don’t get a ContT instance for free, even though ContT has a MonadReader instance in mtl. Unlike the other monad transformers mtl provides, ContT does not have a MonadTransControl instance because it cannot be generally mapped over. While a mapContT function does exist, its signature is more restricted:

                          mapContT :: (m r -> m r) -> ContT k r m a -> ContT k r m a

                          It happens that local can still be implemented for ContT, so it can still have a MonadReader instance, but it cannot be derived in the same way as it can for the other transformers. Still, in practice, I’ve found that most user-defined transformers do not have such complex control flow, so they can safely be instances of MonadTransControl, and they get this deriving for free.

                          Extending this technique to other mtl typeclasses

                          The default instances for the other mtl typeclasses are slightly different from the one for MonadReader, but for the most part, the same general technique applies. Here’s a derivable MonadError:

                          class Monad m => MonadError e m | m -> e where
                          +  throwError :: e -> m a
                          +  default throwError :: (MonadTrans t, MonadError e m1, m ~ t m1) => e -> m a
                          +  throwError = lift . throwError
                          +
                          +  catchError :: m a -> (e -> m a) -> m a
                          +  default catchError :: (MonadTransControl t, MonadError e m1, m ~ t m1) => m a -> (e -> m a) -> m a
                          +  catchError x f = liftWith (\run -> catchError (run x) (run . f)) >>= restoreT . return
                          +
                          +instance MonadError e m => MonadError e (ReaderT r m)
                          +instance (MonadError e m, Monoid w) => MonadError e (WriterT w m)
                          +instance MonadError e m => MonadError e (StateT s m)
                          +instance (MonadError e m, Monoid w) => MonadError e (RWST r w s m)

                          The MonadState interface turns out to be extremely simple, so it doesn’t even need MonadTransControl at all:

                          class Monad m => MonadState s m | m -> s where
                          +  get :: m s
                          +  default get :: (MonadTrans t, MonadState s m1, m ~ t m1) => m s
                          +  get = lift get
                          +
                          +  put :: s -> m ()
                          +  default put :: (MonadTrans t, MonadState s m1, m ~ t m1) => s -> m ()
                          +  put = lift . put
                          +
                          +  state :: (s -> (a, s)) -> m a
                          +  state f = do
                          +    s <- get
                          +    let (a, s') = f s
                          +    put s'
                          +    return a
                          +
                          +instance MonadState s m => MonadState s (ExceptT e m)
                          +instance MonadState s m => MonadState s (ReaderT r m)
                          +instance (MonadState s m, Monoid w) => MonadState s (WriterT w m)

                          Everything seems to be going well! However, not everything is quite so simple.

                          A MonadWriter diversion

                          Unexpectedly, MonadWriter turns out to be by far the trickiest of the bunch. It’s not too hard to create default implementations for most of the methods of the typeclass:

                          class (Monoid w, Monad m) => MonadWriter w m | m -> w where
                          +  writer :: (a, w) -> m a
                          +  default writer :: (MonadTrans t, MonadWriter w m1, m ~ t m1) => (a, w) -> m a
                          +  writer = lift . writer
                          +
                          +  tell :: w -> m ()
                          +  default tell :: (MonadTrans t, MonadWriter w m1, m ~ t m1) => w -> m ()
                          +  tell = lift . tell
                          +
                          +  listen :: m a -> m (a, w)
                          +  default listen :: (MonadTransControl t, MonadWriter w m1, m ~ t m1) => m a -> m (a, w)
                          +  listen x = do
                          +    (y, w) <- liftWith (\run -> listen (run x))
                          +    y' <- restoreT (return y)
                          +    return (y', w)

                          However, MonadWriter has a fourth method, pass, which has a particularly tricky type signature:

                          pass :: m (a, w -> w) -> m a

                          As far as I can tell, this is not possible to generalize using MonadTransControl alone, since it would require inspection of the result of the monadic argument (that is, it would require a function from StT t (a, b) -> (StT t a, b)), which is not possible in general. My gut is that this could likely also be generalized with a slightly more powerful abstraction than MonadTransControl, but it is not immediately obvious to me what that abstraction should be.

                          One extremely simple way to make this possible would be to design something to serve this specific use case:

                          type RunSplit t = forall m a b. Monad m => t m (a, b) -> m (StT t a, Maybe b)
                          +class MonadTransControl t => MonadTransSplit t where
                          +  liftWithSplit :: Monad m => (RunSplit t -> m a) -> t m a

                          Instances of MonadTransSplit would basically just provide a way to pull out bits of the result, if possible:

                          instance MonadTransSplit (ReaderT r) where
                          +  liftWithSplit f = liftWith $ \run -> f (fmap split . run)
                          +    where split (x, y) = (x, Just y)
                          +
                          +instance MonadTransSplit (ExceptT e) where
                          +  liftWithSplit f = liftWith $ \run -> f (fmap split . run)
                          +    where split (Left e) = (Left e, Nothing)
                          +          split (Right (x, y)) = (Right x, Just y)
                          +
                          +instance MonadTransSplit (StateT s) where
                          +  liftWithSplit f = liftWith $ \run -> f (fmap split . run)
                          +    where split ((x, y), s) = ((x, s), Just y)

                          Then, using this, it would be possible to write a generic version of pass:

                          default pass :: (MonadTransSplit t, MonadWriter w m1, m ~ t m1) => m (a, w -> w) -> m a
                          +pass m = do
                          +  r <- liftWithSplit $ \run -> pass $ run m >>= \case
                          +    (x, Just f) -> return (x, f)
                          +    (x, Nothing) -> return (x, id)
                          +  restoreT (return r)

                          However, this seems pretty overkill for just one particular method, given that I have no idea if MonadTransSplit would be useful anywhere else. One interesting thing about going down this rabbit hole, though, is that I learned that pass has some somewhat surprising behavior when mixed with transformers like ExceptT or MaybeT, if you don’t carefully consider how it works. It’s a strange method with a somewhat strange interface, so I don’t think I have a satisfactory conclusion about MonadWriter yet.

                          Regrouping and stepping back

                          Alright, that was a lot of fairly intense, potentially confusing code. What the heck did we actually accomplish? Well, we got a couple of things:

                          1. First, we developed a technique for writing simple mtl-style typeclasses that are derivable using DeriveAnyClass (or simply writing an empty instance declaration). We used a MonadExit class as a proof of concept, but really, the technique is applicable to most mtl-style typeclasses that represent simple effects (including, for example, MonadIO).

                            This technique is useful in isolation, even if you completely disregard the rest of the blog post. For an example where I recently applied it in real code, see the default signatures provided with MonadPersist from the monad-persist library, which make defining instances completely trivial. If you use mtl-style typeclasses in your own application to model effects, I don’t see much of a reason not to use this technique.

                          2. After MonadExit, we applied the same technique to the mtl-provided typeclasses MonadReader, MonadError, and MonadState. These are a bit trickier, since the first two need MonadTransControl in addition to the usual MonadTrans.

                            Whether or not this sort of thing should actually be added to mtl itself probably remains to be seen. For the simplest typeclass, MonadState, it seems like there probably aren’t many downsides, but given the difficulty implementing it for MonadWriter (or, heaven forbid, MonadCont, which I didn’t even seriously take a look at for this blog post), it doesn’t seem like an obvious win. Consistency is important.

                            Another downside that I sort of glossed over is possibly even more significant from a practical point of view: adding default signatures to MonadReader would require the removal of the default implementation of ask that is provided by the existing library (which implements ask in terms of reader). This would be backwards-incompatible, so it’d be difficult to change, even if people wanted to do it. Still, it’s interesting to consider what these typeclasses might look like if they were designed today.

                          Overall, these techniques are not a silver bullet for deriving mtl-style typeclasses, nor do they eliminate the n2 instances problem that mtl style suffers from. That said, they do significantly reduce boilerplate and clutter in the simplest cases, and they demonstrate how modern Haskell’s hierarchy of typeclasses provides a lot of power, both to describe quite abstract concepts and to alleviate the need to write code by hand.

                          I will continue to experiment with the ideas described in this blog post, and I’m sure some more pros and cons will surface as I explore the design space. If you have any suggestions for how to deal with “the MonadWriter problem”, I’d be very interested to hear them! In the meantime, consider using the technique in your application code when writing effectful, monadic typeclasses.

                            \ No newline at end of file diff --git a/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/index.html b/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/index.html new file mode 100644 index 0000000..ad19b02 --- /dev/null +++ b/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/index.html @@ -0,0 +1,27 @@ +Realizing Hackett, a metaprogrammable Haskell

                            Realizing Hackett, a metaprogrammable Haskell

                            Almost five months ago, I wrote a blog post about my new programming language, Hackett, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.

                            Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, Hackett is not only real, it’s working, and you can try it out yourself!

                            A first look at Hackett

                            Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.

                            As Racket law decrees it, every Hackett program must begin with #lang. We can start with the appropriate incantation:

                            #lang hackett

                            If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:

                            #lang hackett
                            +
                            +(main (println "Hello, world!"))

                            In Hackett, a use of main at the top level indicates that running the module as a program should execute some IO action. In this case, println is a function of type {String -> (IO Unit)}. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an IO value. If you run the above program, you will notice that it really does print out Hello, world!, exactly as we would like.

                            Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our own class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?

                            #lang hackett
                            +
                            +(def fibs : (List Integer)
                            +  {0 :: 1 :: (zip-with + fibs (tail! fibs))})
                            +
                            +(main (println (show (take 10 fibs))))

                            Again, Hackett is just like Haskell in that it is lazy, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call take, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!

                            But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably aren’t new to programming. How about something more interesting, like a web server?

                            #lang hackett
                            +
                            +(require hackett/demo/web-server)
                            +
                            +(data Greeting (greeting String))
                            +
                            +(instance (->Body Greeting)
                            +  [->body (λ [(greeting name)] {"Hello, " ++ name ++ "!"})])
                            +
                            +(defserver run-server
                            +  [GET "/"               -> String   => "Hello, world!"]
                            +  [GET "greet" -> String -> Greeting => greeting])
                            +
                            +(main (do (println "Running server on port 8080.")
                            +          (run-server 8080)))
                            $ racket my-server.rkt
                            +Running server on port 8080.
                            +^Z
                            +$ bg
                            +$ curl 'http://localhost:8080/greet/Alexis'
                            +Hello, Alexis!

                            Welcome to Hackett.

                            What is Hackett?

                            Excited yet? I hope so. I certainly am.

                            Before you get a little too excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.

                            All that said, it is a real tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:

                            • Algebraic datatypes. Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).

                            • Typeclasses. The demo web server uses a ->Body typeclass to render server responses, and this module implements a ->Body instance for the custom Greeting datatype.

                            • Macros. The defserver macro provides a concise, readable, type safe way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.

                            • Static typechecking. Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the ->Body instance and see what happens.

                            • Infix operators. In Hackett, { curly braces } enter infix mode, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.

                            • Pure, monadic I/O. The println and run-server functions both produce (IO Unit), and IO is a monad. do notation is provided as a macro, and it works with any type that implements the Monad typeclass.

                            All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp. Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.

                            For a bit more information about what Hackett is and what it aims to be, check out my blog post from a few months ago from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.

                            The story so far, and getting to Hackett 0.1

                            In September of 2016, I attended (sixth RacketCon), where I saw a pretty incredible and extremely exciting talk about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.

                            Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!

                            …it sort of worked?

                            The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.

                            At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.

                            Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from Sam Tobin-Hochstadt and put together an implementation of Pierce and Turner’s Local Type Inference. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism. After that, things just sort of started falling into place:

                            Less than three weeks later, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with editor support. The future of Hackett looks bright, and though there’s a lot of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.

                            So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly no, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?

                            What Hackett still isn’t

                            I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).

                            Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.

                            Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like Show and Eq is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.

                            Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so let and letrec need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.

                            Oh, and of course, the whole thing needs to be documented. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.

                            All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.

                            Answering some questions

                            It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.

                            Can I try Hackett?

                            Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you do want to give it a try, it isn’t difficult: just install Racket, then run raco pkg install hackett. Open DrRacket and write #lang hackett at the top of the module, then start playing around.

                            Also, note that the demo web server used in the example at the top of this blog post is not included when you install the hackett package. If you want to try that out, you’ll have to run raco pkg install hackett-demo to install the demo package as well.

                            Are there any examples of Hackett code?

                            Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can find it on GitHub here, or you can open the hackett/private/prim/base module on a local installation.

                            How can I learn more / ask questions about Hackett?

                            Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (which you can sign up for here), sending me a DM on Twitter, opening an issue on the GitHub repo, or even just sending me an email (though I’m usually a bit slower to respond to the latter).

                            How can I help?

                            Probably the easiest way to help out is to try Hackett for yourself and report any bugs or infelicities you run into. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.

                            If you are interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.

                            How does Hackett compare to X / why doesn’t Hackett support Y?

                            These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.

                            When will Hackett be ready for me to use?

                            I don’t know.

                            Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).

                            However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.

                            Appendix

                            It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to Stephen Chang, Joshua Dunfield, Robby Findler, Matthew Flatt, Phil Freeman, Ben Greenman, Alex Knauth, Neelakantan Krishnaswami, and Sam Tobin-Hochstadt. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.

                            As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is on theme with the name, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:

                            • The Beach Boys — Pet Sounds

                            • Boards of Canada — Music Has The Right To Children, Geogaddi

                            • Bruce Springsteen — Born to Run

                            • King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline

                            • Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail

                            • Mahavishnu Orchestra — Birds of Fire

                            • Metric — Fantasies, Synthetica, Pagans in Vegas

                            • Muse — Origin of Symmetry, Absolution, The Resistance

                            • Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up

                            • Pink Floyd — Wish You Were Here

                            • Supertramp — Breakfast In America

                            • The Protomen — The Protomen, Act II: The Father of Death

                            • Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light

                            • Yes — Fragile, Relayer, Going For The One

                            And of course, Voyage of the Acolyte, by Steve Hackett.

                              \ No newline at end of file diff --git a/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/index.html b/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/index.html new file mode 100644 index 0000000..e101bda --- /dev/null +++ b/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/index.html @@ -0,0 +1,72 @@ +Unit testing effectful Haskell with monad-mock

                              Unit testing effectful Haskell with monad-mock

                              ⦿ haskell, testing

                              Nearly eight months ago, I wrote a blog post about unit testing effectful Haskell code using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, monad-mock.

                              A first glance at monad-mock

                              The monad-mock library is, first and foremost, designed to be easy. It doesn’t ask much from you, and it requires almost zero boilerplate.

                              The first step is to write an mtl-style interface that encodes an effect you want to mock. For example, you might want to test some code that interacts with the filesystem:

                              class Monad m => MonadFileSystem m where
                              +  readFile :: FilePath -> m String
                              +  writeFile :: FilePath -> String -> m ()

                              Now you just have to write your code as normal. For demonstration purposes, here’s a function that defines copying a file in terms of readFile and writeFile:

                              copyFile :: MonadFileSystem m => FilePath -> FilePath -> m ()
                              +copyFile a b = do
                              +  contents <- readFile a
                              +  writeFile b contents

                              Making this function work on the real filesystem is trivial, since we just need to define an instance of MonadFileSystem for IO:

                              instance MonadFileSystem IO where
                              +  readFile = Prelude.readFile
                              +  writeFile = Prelude.writeFile

                              But how do we test this? Well, we could run some real code in IO, which might not be so bad for such a simple function, but this seems like a bad idea. For one thing, a bad implementation of copyFile could do some pretty horrible things if it misbehaved and decided to overwrite important files, and if you’re constantly running a test suite whenever a file changes, it’s easy to imagine causing a lot of damage. Running tests against the real filesystem also makes tests slower and harder to parallelize, and it only gets much worse once you are doing more complex effects than interacting with the filesystem.

                              Using monad-mock, we can test this function in just a couple of lines of code:

                              import Control.Exception (evaluate)
                              +import Control.Monad.Mock
                              +import Control.Monad.Mock.TH
                              +import Data.Function ((&))
                              +import Test.Hspec
                              +
                              +makeMock "FileSystemAction" [ts| MonadFileSystem |]
                              +
                              +spec = describe "copyFile" $
                              +  it "reads a file and writes its contents to another file" $
                              +    evaluate $ copyFile "foo.txt" "bar.txt"
                              +      & runMock [ ReadFile "foo.txt" :-> "contents"
                              +                , WriteFile "bar.txt" "contents" :-> () ]

                              That’s it!

                              The last two lines of the above snippet are the real interesting bits, which specify the actions that are expected to be executed, and it couples them with their results. You will find that if you tweak the list in any way, such as reordering the actions, eliminating one or both of them, or adding an additional action to the end, the test will fail. We could even turn this into a property-based test that generated arbitrary file paths and file contents.

                              Admittedly, in this trivial example, the mock is a little silly, since converting this into a property-based test would demonstrate how much we’ve basically just reimplemented the function in our test. However, once our function starts to do somewhat more complicated things, then our tests become more meaningful. Here’s a similar function that only copies a file if it is nonempty:

                              copyNonemptyFile :: MonadFileSystem m => FilePath -> FilePath -> m ()
                              +copyNonemptyFile a b = do
                              +  contents <- readFile a
                              +  unless (null contents) $
                              +    writeFile b contents

                              This function has some logic which is very clearly not expressed in its type, and it would be difficult to encode that information into the type in a safe way. Fortunately, we can guarantee that it works by writing some tests:

                              describe "copyNonemptyFile" $ do
                              +  it "copies a file with contents" $
                              +    evaluate $ copyNonemptyFile "foo.txt" "bar.txt"
                              +      & runMock [ ReadFile "foo.txt" :-> "contents"
                              +                , WriteFile "bar.txt" "contents" :-> () ]
                              +
                              +  it "does nothing with an empty file" $
                              +    evaluate $ copyNonemptyFile "foo.txt" "bar.txt"
                              +      & runMock [ ReadFile "foo.txt" :-> "" ]

                              These tests are much more useful, and they have some actual value to them. Imagine we had accidentally written when instead of unless, an easy typo to make. Our tests would fail with some useful error messages:

                              1) copyNonemptyFile copies a file with contents
                              +     uncaught exception: runMockT: expected the following unexecuted actions to be run:
                              +       WriteFile "bar.txt" "contents"
                              +
                              +2) copyNonemptyFile does nothing with an empty file
                              +     uncaught exception: runMockT: expected end of program, called writeFile
                              +       given action: WriteFile "bar.txt" ""
                              +

                              You now know enough to write tests with monad-mock.

                              Why unit test?

                              When the issue of testing is brought up in Haskell, it is often treated with a certain distaste by a portion of the community. There are some points I’ve seen a number of times, and though they take different forms, they boil down to two ideas:

                              1. “Haskell code does not need tests because the type system can prove correctness.”

                              2. “Testing in Haskell is trivial because it is a pure language, and testing pure functions is easy.”

                              I’ve been writing Haskell professionally for over a year now, and I can happily say that there is some truth to both of those things! When my Haskell code typechecks, I feel a confidence in it that I would not feel were I using a language with a less powerful type system. Furthermore, Haskell encourages a “pure core, impure shell” approach to system design that makes testing many things pleasant and straightforward, and it completely eliminates the worry of subtle nondeterminism leaking into tests.

                              That said, Haskell is not a proof assistant, and its type system cannot guarantee everything, especially for code that operates on the boundaries of what Haskell can control. For much the same reason, I find that my pure code is the code I am least likely to need to test, since it is also the code with the strongest type safety guarantees, operating on types in my application’s domain. In contrast, the effectful code is often what I find the most value in extensively testing, since it often contains the most subtle complexity, and it is frequently difficult or even impossible to encode into types.

                              Haskell has the power to provide remarkably strong correctness guarantees with a surprisingly small amount of effort by using a combination of tests and types, using each to accommodate for the other’s weaknesses and playing to each technique’s strengths. Some code is test-driven, other code is type-driven. Most code ends up being a mix of both. Testing is just a tool like any other, and it’s nice to feel confident in one’s ability to effectively structure code in a decoupled, testable manner.

                              Why mock?

                              Even if you accept that testing is good, the question of whether or not to mock is a subtler issue. To some people, “unit testing” is synonymous with mocks. This is emphatically not true, and in fact, overly aggressive mocking is one of the best ways to make your test suite completely worthless. The monad-mock approach to mocking is a bit more principled than mocking in many dynamic, object-oriented languages, but it comes with many of the same drawbacks: mocks couple your tests to your implementation in ways that make them less valuable and less meaningful.

                              For the MonadFileSystem example above, I would actually probably not use a mock. Instead, I would use a fake, in-memory filesystem implementation:

                              newtype FakeFileSystemT m a = FakeFileSystemT (StateT [(FilePath, String)] m a)
                              +  deriving (Functor, Applicative, Monad)
                              +
                              +fakeFileSystemT :: Monad m => [(FilePath, String)]
                              +                -> FakeFileSystemT m a -> m (a, [(FilePath, String)])
                              +fakeFileSystemT fs (FakeFileSystemT x) = second sort <$> runStateT x fs
                              +
                              +instance Monad m => MonadFileSystem (FakeFileSystemT m) where
                              +  readFile path = FakeFileSystemT $ get >>= \fs -> lookup path fs &
                              +    maybe (fail $ "readFile: no such file ‘" ++ path ++ "’") return
                              +  writeFile path contents = FakeFileSystemT . modify $ \fs ->
                              +    (path, contents) : filter ((/= path) . fst) fs

                              The above snippet demonstrates how easy it is to define a MonadFileSystem implementation in terms of StateT, and while this may seem like a lot of boilerplate, it really isn’t. You have to write a fake once per interface, and the above block is a minuscule twelve lines of code. With this technique, you are still able to write tests that depend on the state of the filesystem before and after running the implementation, but you decouple yourself from the precise process of getting there:

                              describe "copyNonemptyFile" $ do
                              +  it "copies a file with contents" $ do
                              +    let ((), fs) = runIdentity $ copyNonemptyFile "foo.txt" "bar.txt"
                              +          & fakeFileSystemT [ ("foo.txt", "contents") ]
                              +    fs `shouldBe` [ ("bar.txt", "contents"), ("foo.txt", "contents") ]
                              +
                              +  it "does nothing with an empty file" $ do
                              +    let ((), fs) = runIdentity $ copyNonemptyFile "foo.txt" "bar.txt"
                              +          & fakeFileSystemT [ ("foo.txt", "") ]
                              +    fs `shouldBe` [ ("foo.txt", "") ]

                              This is better than using a mock, and I would highly recommend doing it if you can! However, a lot of real applications have to interact with services of much greater complexity than an idealized filesystem, and creating that sort of in-memory fake is not always practical. One such situation might be interacting with AWS CloudFormation, for example:

                              class Monad m => MonadAWS m where
                              +  createStack :: StackName -> StackTemplate -> m (Either AWSError StackId)
                              +  listStacks :: m (Either AWSError [StackSummaries])
                              +  describeStack :: StackId -> m (Either AWSError StackInfo)
                              +  -- and so on...

                              AWS is a very complex system, and it can do dozens of different things (and fail in dozens of different ways) based on an equally complex set of inputs. For example, in the above API, createStack needs to parse its template, which can be YAML or JSON, in order to determine which of many possible errors and behaviors can be produced, both on the initial call and on subsequent ones.

                              Creating a fake implementation of AWS is hardly feasible, and this is where a mock can be useful. By simply writing makeMock "AWSAction" [ts| MonadAWS |], we can test functions that interact with AWS in a pure way without necessarily needing to replicate all of its complexity.

                              Isolating mocks

                              Of course, tests that use mocks provide less value than tests that use “smarter” fakes, since they are far more tightly coupled to the implementation, and it’s dramatically more likely that you will need to change the tests when you change the logic. To avoid this, it can be helpful to create multiple interfaces to the same thing: a high-level interface and a low-level one. If our above MonadAWS is a low-level interface, we could create a high-level counterpart that does precisely what our application needs:

                              class Monad m => MonadDeploy m where
                              +  executeDeployment :: Deployment -> m (Either DeployError ())

                              When running our application “for real”, we would use MonadAWS to implement MonadDeploy:

                              executeDeploymentImpl :: MonadAWS m => Deployment -> m (Either DeployError ())
                              +executeDeploymentImpl = ...

                              The nice thing about this is we can actually test executeDeploymentImpl using a MonadAWS mock, so we can still have unit test coverage of the code on the boundaries of our system! Additionally, by containing the mock to a single place, we can test the rest of our code using a smarter fake implementation of MonadDeploy, helping to decouple our code from AWS’s complex API and improve the reliability and usefulness of our test suite.

                              They key point here is that mocking is just a small piece of the larger testing puzzle in any language, and that is just as true in Haskell. An overemphasis on mocking is an easy way to end up with a test suite that feels useless, probably because it is. Use mocks as a technique to insulate your application from the complexity in others’ APIs, then use more domain-specific testing techniques and type-level assertions to ensure the correctness of your logic.

                              How monad-mock works

                              If you’ve read this far and are convinced that monad-mock is useful, you may safely stop reading now. However, if you are interested in the details of what it actually does and what makes it tick, the rest of this blog post is going to focus on how the implementation works and how it compares to other techniques.

                              The centerpiece of monad-mock’s API is its monad transformer, MockT, which is a type constructor that accepts three types:

                              newtype MockT (f :: * -> *) (m :: * -> *) (a :: *)

                              The m and a type variables obviously correspond to the usual monad transformer arguments, which represent the underlying monad and the result of the monadic computation, respectively. The f variable is more interesting, since it’s what makes MockT work at all, and it isn’t even a type: it’s a type constructor with kind * -> *. What does it mean?

                              Looking at the type signature of runMockT gives us a little bit more information about what that f actually represents:

                              runMockT :: (Action f, Monad m) => [WithResult f] -> MockT f m a -> m a

                              This type signature provides two pieces of key information:

                              1. The f parameter is constrained by the Action f constraint.

                              2. Running a mocked computation requires supplying a list of WithResult f values. This list corresponds to the list of expectations provided to runMock in earlier examples.

                              To understand both of these things, it helps to examine the definition of an actual datatype that can have an Action instance. For the filesystem example, the action datatype looks like this:

                              data FileSystemAction r where
                              +  ReadFile :: FilePath -> FileSystemAction String
                              +  WriteFile :: FilePath -> String -> FileSystemAction ()

                              Notice how each constructor clearly corresponds to one of the methods of MonadFileSystem, with a type to match. Now the purpose of the type provided to the FileSystemAction constructor (in this case r) should hopefully become clear: it represents the type of the value produced by each method. Also note that the type is completely phantom—it does not appear in negative position in any of the constructors.

                              With this in mind, we can take a look at the definition of WithResult:

                              data WithResult f where
                              +  (:->) :: f r -> r -> WithResult f

                              This is what defines the (:->) constructor from earlier in the blog post, and you can see that it effectively just represents a tuple of an action and a value of its associated result. It’s completely type-safe, since it ensures the result matches the type argument to the action.

                              Finally, this brings us to the Action class, which is not complex, but is unfortunately necessary:

                              class Action f where
                              +  eqAction :: f a -> f b -> Maybe (a :~: b)
                              +  showAction :: f a -> String

                              Notice that these methods are effectively just (==) and show, lifted to type constructors of kind * -> *. One significant difference is that eqAction produces Maybe (a :~: b) instead of Bool, where (:~:) is from Data.Type.Equality. This is a type equality witness, which means a successful equality between two values allows the compiler to be sure that the two types are equal. This is necessary for the implementation of runMockT due to the phantom type in actions—in order to convince GHC that we can properly return the result of a mocked action, we need to assure it that the value we’re going to return is actually of the proper type.

                              Implementing this typeclass is not particularly burdensome, but it’s entirely boilerplate, so even if you want to define your own action type (that is, you don’t want to use makeMock), you can use the deriveAction function from Control.Monad.Mock.TH to derive an Action instance on an existing datatype.

                              Connecting the mock to its class

                              Now that we have an action with which to mock a class, we need to actually define an instance of that class for MockT. For this process, monad-mock provides a mockAction function with the following type:

                              mockAction :: (Action f, Monad m) => String -> f r -> MockT f m r

                              This function accepts two arguments: the name of the method being mocked and the action that represents the current call. This is easier to illustrate with an actual instance of MonadFileSystem using MockT and our FileSystemAction type:

                              instance Monad m => MonadFileSystem (MockT FileSystemAction m) where
                              +  readFile a = mockAction "readFile" (ReadFile a)
                              +  writeFile a b = mockAction "writeFile" (WriteFile a b)

                              This allows readFile and writeFile to defer to the mock, and providing the names of the functions as strings helps monad-mock to produce useful error messages upon failure. Internally, MockT is a StateT that keeps track of a list of WithResult f values as its state. Each call to the mock checks the action against the internal list of calls, and if they match, it returns the associated result. Otherwise, it throws an exception.

                              This scheme is simple, but it seems to work remarkably well. There are some obvious enhancements that will probably be eventually necessary, like allowing action results that run in the underlying monad m in order to support things like throwError from MonadError, but so far, it hasn’t been necessary for what we’ve been using it for. Certain tricky signatures defy this simple technique, such as signatures where a monadic action appears in a negative position (that is, the signatures you need things like monad-control or monad-unlift for), but we’ve found that most of our effects don’t have any reason to include such signatures.

                              A brief comparison with free(r) monads

                              At this point, astute readers will likely be thinking about free monads, which parts of this technique greatly resemble. The representation of actions as GADTs is especially similar to freer, which does something extremely similar. Indeed, you can think of this technique as something that combines a freer-style representation with mtl-style classes. Given that freer already does this, you might ask yourself what the point is.

                              If you are already sold on free monads, monad-mock may very well be uninteresting to you. From the perspective of theoretical novelty, monad-mock is not anything new or different. However, there are a variety of practical reasons to prefer mtl over free, and it’s nice to see how easy it is to enjoy the testing benefits of free without too much extra effort.

                              An in-depth comparison between mtl and free is well outside the scope of this blog post. However, the key point is that this technique only affects test code, so the real runtime implementation will not be affected in any way. This means you can take advantage of the performance benefits and ecosystem support of mtl without sacrificing simple, expressive testing.

                              Conclusion

                              To cap things off, I want to emphasize monad-mock’s role as a single part of a larger initiative we’ve been making for the better part of the past eighteen months. Haskell is a language with ever-evolving techniques and style, and it’s sometimes dizzying to figure out how to use all the pieces together to develop robust, maintainable applications. While monad-mock might not be anything drastically different from existing testing techniques, my hope is that it can provide an opinionated mechanism to make testing easy and accessible, even for complex interactions with other services and systems.

                              I’ve made an effort to make it abundantly clear in this blog post that monad-mock is not a silver bullet to testing, and in fact, I would prefer other techniques for ensuring correctness whenever possible. Even so, mocking is a nice tool to have in your toolbox, and it’s a good fallback to get even the worst APIs under test coverage.

                              If you want to try out monad-mock for yourself, take a look at the documentation on Hackage and start playing around! It’s still early software, so it’s not the most proven or featureful, but we’ve managed to get mileage out of it already, all the same. If you find any problems, have a use case it does not support, or just find something about it unclear, please do not hesitate to open an issue on the GitHub repository—we obviously can’t fix issues we don’t know about.

                              Thanks as always to the many people who have contributed ideas that have shaped my philosophy and approach to testing and have helped provide the tools that make this library work. Happy testing!

                                \ No newline at end of file diff --git a/blog/2017/08/12/user-programmable-infix-operators-in-racket/index.html b/blog/2017/08/12/user-programmable-infix-operators-in-racket/index.html new file mode 100644 index 0000000..d08cf80 --- /dev/null +++ b/blog/2017/08/12/user-programmable-infix-operators-in-racket/index.html @@ -0,0 +1,139 @@ +User-programmable infix operators in Racket

                                User-programmable infix operators in Racket

                                ⦿ racket, hackett, macros

                                Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in Hackett, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.

                                Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done without modifying the stock #lang racket reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.

                                Our mission

                                Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we do want:

                                • Infix operators should be user-extensible, not limited to a special set of built-in operators.

                                • Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.

                                • We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like cons. This allows 5 - 1 - 2 to be parsed as (- (- 5 1) 2), but 5 :: 1 :: nil to be parsed as (:: 5 (:: 1 nil)).

                                These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:

                                • We will not permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing (+ 1 2) with {1 + 2}.

                                • Our implementation will not support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.

                                • All operators will be binary, and we will not support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.

                                With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:

                                #lang racket
                                +
                                +(require (prefix-in racket/base/ racket/base)
                                +         "infix.rkt")
                                +
                                +(define-infix-operator - racket/base/- #:fixity left)
                                +(define-infix-operator :: cons #:fixity right)
                                +
                                +{{2 - 1} :: {10 - 3} :: '()}
                                +; => '(1 7)

                                Let’s get started.

                                Implementing infix operators

                                Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:

                                1. How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?

                                2. How can we associate fixity information with certain operators?

                                We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.

                                So, how do we detect if a Racket expression is surrounded by curly braces? Normally, in #lang racket, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated exactly the same as parentheses:

                                > {+ 1 2}
                                +3

                                If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.

                                Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as syntax objects. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as syntax properties. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s reader (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named 'paren-shape.

                                This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:

                                > (syntax-property #'(1 2 3) 'paren-shape)
                                +#f
                                +> (syntax-property #'[1 2 3] 'paren-shape)
                                +#\[
                                +> (syntax-property #'{1 2 3} 'paren-shape)
                                +#\{

                                This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.

                                Customizing application

                                Racket is a language designed to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named #%app, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:

                                (+ 1 2)

                                …into this:

                                (#%app + 1 2)

                                What’s special about #%app is that the macroexpander will use whichever #%app is in scope in the expression’s lexical context, so if we write our own version of #%app, it will be used instead of the one from #lang racket. This is what we will use to hook into ordinary Racket expressions.

                                To write our custom version of #%app, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, syntax/parse. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the 'paren-shape syntax property, syntax/parse/class/paren-shape. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:

                                #lang racket
                                +
                                +(require (for-syntax syntax/parse/class/paren-shape)
                                +         (prefix-in racket/base/ racket/base)
                                +         syntax/parse/define)
                                +
                                +(define-syntax-parser #%app
                                +  [{~braces _ arg ...}
                                +   #'(#%infix arg ...)]
                                +  [(_ arg ...)
                                +   #'(racket/base/#%app arg ...)])

                                This code will transform any applications surrounded in curly braces into one that starts with #%infix instead of #%app, so {1 + 2} will become (#%infix 1 + 2), for example. The identifier #%infix isn’t actually special in any way, it just has a funny name, but we haven’t actually defined #%infix yet, so we need to do that next!

                                To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like {1 + 2}, should be converted into the equivalent prefix expressions, in this case (+ 1 2). We can do this with a simple macro:

                                (define-syntax-parser #%infix
                                +  [(_ a op b)
                                +   #'(racket/base/#%app op a b)])

                                Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the #%app from racket/base, which will avoid any accidental infinite recursion between our #%app and #%infix. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:

                                > (+ 1 2)
                                +3
                                +> {1 + 2}
                                +3

                                That’s pretty cool!

                                Of course, we probably want to support infix applications with more than just a single binary operator, such as {1 + 2 + 3}. We can implement that just by adding another case to #%infix that handles more subforms:

                                (define-syntax-parser #%infix
                                +  [(_ a op b)
                                +   #'(racket/base/#%app op a b)]
                                +  [(_ a op b more ...)
                                +   #'(#%infix (#%infix a op b) more ...)])

                                …and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:

                                > {1 + 2 + 3}
                                +6
                                +> {1 + 2 + 3 + 4}
                                +10

                                I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:

                                > {1 + 2 * 3 - 4}
                                +5

                                Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as cons, nested -> types or contracts for curried functions, and expt, the exponentiation operator.

                                Tracking operator fixity

                                Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.

                                Essentially, Racket (like Scheme) uses a define-syntax form to define macros, which is what define-syntax-parser eventually expands into. However, unlike Scheme, Racket’s define-syntax is not just for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:

                                (define-syntax foo 3)

                                Once a binding has been defined using define-syntax, a macro can look up the value associated with it by using the syntax-local-value function, which returns the compile-time value associated with an identifier:

                                (begin-for-syntax
                                +  (println (syntax-local-value #'foo)))
                                +; => 3

                                The cool thing is that syntax-local-value gets the value associated with a specific binding, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use syntax-local-value to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the foo binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because foo is not bound to anything at runtime.

                                To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the prop:procedure structure type property.

                                If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:

                                (require (for-syntax syntax/transformer))
                                +
                                +(begin-for-syntax
                                +  (struct infix-operator (runtime-binding fixity)
                                +    #:property prop:procedure
                                +    (λ (operator stx)
                                +      ((set!-transformer-procedure
                                +        (make-variable-like-transformer
                                +         (infix-operator-runtime-binding operator)))
                                +       stx))))

                                This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for runtime-binding, but we can also include a value of our choosing for fixity. Here’s an example:

                                (define-syntax :: (infix-operator #'cons 'right))

                                This new :: binding will act, in every way, just like cons. If we use it in the REPL, you can see that it acts exactly the same:

                                > (:: 1 '())
                                +'(1)

                                However, we can also use syntax-local-value to extract this binding’s fixity at compile-time, and that’s what makes it interesting:

                                (begin-for-syntax
                                +  (println (infix-operator-fixity (syntax-local-value #'::))))
                                +; => 'right

                                Using this extra compile-time information, we can adjust our #%infix macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used syntax/parse/class/paren-shape to make decisions based on the 'paren-shape syntax property, we can use syntax/parse/class/local-value to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:

                                (begin-for-syntax
                                +  (define-syntax-class infix-op
                                +    #:description "infix operator"
                                +    #:attributes [fixity]
                                +    [pattern {~var op (local-value infix-operator?)}
                                +             #:attr fixity (infix-operator-fixity (attribute op.local-value))]))

                                Now, we can update #%infix to use our new infix-op syntax class:

                                (define-syntax-parser #%infix
                                +  [(_ a op:infix-op b)
                                +   #'(racket/base/#%app op a b)]
                                +  [(_ a op:infix-op b more ...)
                                +   #:when (eq? 'left (attribute op.fixity))
                                +   #'(#%infix (#%infix a op b) more ...)]
                                +  [(_ more ... a op:infix-op b)
                                +   #:when (eq? 'right (attribute op.fixity))
                                +   #'(#%infix more ... (#%infix a op b))])

                                Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via #:when clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of #%infix, we can successfully use :: in an infix expression, and it will be parsed with the associativity that we expect:

                                > {1 :: 2 :: 3 :: '()}
                                +'(1 2 3)

                                Exciting!

                                A nicer interface for defining infix operators

                                We currently have to define infix operators by explicitly using define-syntax, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the define-infix-operator form from the example at the very beginning of this blog post.

                                Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the define-syntax definitions we were already writing:

                                (define-simple-macro (define-infix-operator op:id value:id
                                +                       #:fixity {~and fixity {~or {~datum left} {~datum right}}})
                                +  (define-syntax op (infix-operator #'value 'fixity)))

                                With this in hand, we can define some infix operators with a much nicer syntax:

                                (define-infix-operator + racket/base/+ #:fixity left)
                                +(define-infix-operator - racket/base/- #:fixity left)
                                +(define-infix-operator * racket/base/* #:fixity left)
                                +(define-infix-operator / racket/base// #:fixity left)
                                +
                                +(define-infix-operator ^ expt #:fixity right)
                                +(define-infix-operator :: cons #:fixity right)

                                With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary #lang racket:

                                > {1 + 2 - 4}
                                +-1
                                +> {2 ^ 2 ^ 3}
                                +256
                                +> {{2 ^ 2} ^ 3}
                                +64

                                And you know what’s most amazing about this? The entire thing is only 50 lines of code. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:

                                #lang racket
                                +
                                +(require (for-syntax syntax/parse/class/local-value
                                +                     syntax/parse/class/paren-shape
                                +                     syntax/transformer)
                                +         (prefix-in racket/base/ racket/base)
                                +         syntax/parse/define)
                                +
                                +(begin-for-syntax
                                +  (struct infix-operator (runtime-binding fixity)
                                +    #:property prop:procedure
                                +    (λ (operator stx)
                                +      ((set!-transformer-procedure
                                +        (make-variable-like-transformer
                                +         (infix-operator-runtime-binding operator)))
                                +       stx)))
                                +
                                +  (define-syntax-class infix-op
                                +    #:description "infix operator"
                                +    #:attributes [fixity]
                                +    [pattern {~var op (local-value infix-operator?)}
                                +             #:attr fixity (infix-operator-fixity (attribute op.local-value))]))
                                +
                                +(define-syntax-parser #%app
                                +  [{~braces _ arg ...}
                                +   #'(#%infix arg ...)]
                                +  [(_ arg ...)
                                +   #'(racket/base/#%app arg ...)])
                                +
                                +(define-syntax-parser #%infix
                                +  [(_ a op:infix-op b)
                                +   #'(racket/base/#%app op a b)]
                                +  [(_ a op:infix-op b more ...)
                                +   #:when (eq? 'left (attribute op.fixity))
                                +   #'(#%infix (#%infix a op b) more ...)]
                                +  [(_ more ... a op:infix-op b)
                                +   #:when (eq? 'right (attribute op.fixity))
                                +   #'(#%infix more ... (#%infix a op b))])
                                +
                                +(define-simple-macro (define-infix-operator op:id value:id
                                +                       #:fixity {~and fixity {~or {~datum left} {~datum right}}})
                                +  (define-syntax op (infix-operator #'value 'fixity)))
                                +
                                +(define-infix-operator + racket/base/+ #:fixity left)
                                +(define-infix-operator - racket/base/- #:fixity left)
                                +(define-infix-operator * racket/base/* #:fixity left)
                                +(define-infix-operator / racket/base// #:fixity left)
                                +
                                +(define-infix-operator ^ expt #:fixity right)
                                +(define-infix-operator :: cons #:fixity right)

                                Racket is a hell of a programming language.

                                Applications, limitations, and implications

                                This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am almost certain that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even locally.

                                What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as let-syntax and syntax-parameterize. Using these tools, it would be entirely possible to implement a with-fixity macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make / right associative within a block of code:

                                > {1 / 2 / 3}
                                +1/6
                                +> (with-fixity ([/ right])
                                +    {1 / 2 / 3})
                                +1 1/2

                                In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of splicing-let and splicing-let-syntax:

                                (define-simple-macro
                                +  (with-fixity ([op:id {~and fixity {~or {~datum left} {~datum right}}}] ...)
                                +    body ...)
                                +  #:with [op-tmp ...] (generate-temporaries #'[op ...])
                                +  (splicing-let ([op-tmp op] ...)
                                +    (splicing-let-syntax ([op (infix-operator #'op-tmp 'fixity)] ...)
                                +      body ...)))

                                This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations between operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.

                                As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in Hackett to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow any expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.

                                If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at this file for the definition of infix bindings, as well as this file for the defintion of infix application. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like {_ - 1} to serve as a shorthand for (lambda [x] {x - 1}), but I haven’t yet decided if I like the tradeoffs involved.

                                It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in #lang racket without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.

                                Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to #lang hackett, there’s no reason something similar but more powerful couldn’t be built as a separate library by a user of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.

                                  \ No newline at end of file diff --git a/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/index.html b/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/index.html new file mode 100644 index 0000000..05dc6c2 --- /dev/null +++ b/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/index.html @@ -0,0 +1,70 @@ +Hackett progress report: documentation, quality of life, and snake

                                  Hackett progress report: documentation, quality of life, and snake

                                  ⦿ hackett, racket, haskell

                                  Three months ago, I wrote a blog post describing my new, prototype implementation of my programming language, Hackett. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce the first (albeit quite incomplete) approach to Hackett’s documentation.

                                  I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.

                                  A philosophy of documentation

                                  Racket, as a project, has always had wonderful documentation. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably Scribble, the Racket documentation tool. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with a powerful library for documenting Racket code. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.

                                  Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an @ character instead of \. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use the provided defproc form:

                                  @defproc[(add1 [z number?]) number?]{
                                  +Returns @racket[(+ z 1)].}

                                  This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into something like this:

                                  The fact that Scribble documents are fully-fledged programs equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is the scribble/example module, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special eval:check form that enables doctest-like checking, which allows documentation to serve double duty as a test suite.

                                  Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is designed with the goal of supporting many different programming languages, and Scribble is no exception. Things like scribble/example essentially work out of the box with Hackett, and most of scribble/manual can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The defproc and defstruct forms are hardly builtins; they are defined as part of the scribble/manual library in terms of Scribble primitives, and we can do the same.

                                  Hackett’s documentation already defines three new forms, defdata, defclass, and defmethod, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the Functor typeclass using defclass like this:

                                  @defclass[(Functor f)
                                  +          [map : (forall [a b] {(a -> b) -> (f a) -> (f b)})]]{
                                  +
                                  +A class of types that are @deftech{functors}, essentially types that provide a
                                  +mapping or “piercing” operation. The @racket[map] function can be viewed in
                                  +different ways:
                                  +
                                  +...}

                                  With only a little more than the above code, Hackett’s documentation includes a beautifully-typeset definition of the Functor typeclass, including examples and rich prose:

                                  Scribble makes Hackett’s documentation shine.

                                  A tale of two users

                                  For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.

                                  A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.

                                  However, for experienced users, an authoritative reference is exactly what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.

                                  This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:

                                  • Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.

                                    Java’s API documentation documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly small language that does not often change.

                                    On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java does provide guide-style documentation in the form of the Java Tutorials, but they are of inconsistent quality.

                                    More importantly, while the Java tutorials link to the API docs, the reverse is not true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.

                                    Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.

                                  • Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.

                                    The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is MDN. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.

                                    The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.

                                    This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. Ramda’s documentation is nothing but a reference, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, Passport’s docs are essentially only a set of tutorials, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method does. Fortunately, there are some libraries, like React, that absolutely nail this, and they have both styles of documentation that are actually cross-referenced. Unfortunately, those are mostly the exceptions, not the norm.

                                  • Python’s documentation is interesting, since it includes a set of tutorials alongside the API reference, and it also ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on if links to the section in the reference about if, but nothing goes in the other direction, which is something of a missed opportunity.

                                  • Haskell manages to be especially bad here (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.

                                    It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look.

                                  The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with two pieces of documentation: the Racket Guide and the Racket Reference. The guide includes over one hundred thousand words of explanations and examples, and the reference includes roughly half a million. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.

                                  Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the Hackett Guide and the Hackett Reference, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily need to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a lot more accessible without making it any less useful for power users.

                                  Rounding Hackett’s library, sanding its edges

                                  One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a lot of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.

                                  Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:

                                  • Hackett includes a Double type for working with IEEE 754 double-precision floating-point numbers.

                                  • Local definitions are supported via the let and letrec forms.

                                  • The prelude includes many more functions, especially functions on lists.

                                  • The Hackett reader has been adjusted to support using . as a bare symbol, since . is the function composition operator.

                                  • The Hackett REPL supports many more forms, including ADT, class, and instance definitions. Additionally, the REPL now uses Show instances to display the results of expressions. To compensate for the inability to print non-Showable things, a new (#:type expr) syntax is permitted to print the type of any expression.

                                  • Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.

                                  Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now heavily commented with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.

                                  One of the most exciting things about documenting Hackett has been realizing just how much already exists. Seriously, if you have gotten to this point in the blog post but haven’t read the actual documentation yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for me: it’s a language I’d like to use.

                                  Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!

                                  A demo with pictures

                                  Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?

                                  Hackett ships with a special package of demo libraries in the aptly-named hackett-demo package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In the previous Hackett blog post, I demonstrated the capabilities of hackett/demo/web-server. In this blog post, we’re going to use hackett/demo/pict and hackett/demo/pict/universe, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!

                                  As always, we’ll start with #lang hackett, and we’ll import the necessary libraries:

                                  #lang hackett
                                  +
                                  +(require hackett/demo/pict
                                  +         hackett/demo/pict/universe)

                                  With that, we can start immediately with a tiny example. Just to see how hackett/demo/pict works, let’s start by rendering a red square. We can do this by writing a main action that calls print-pict:

                                  (main (print-pict (colorize red (filled-square 50.0))))

                                  If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!

                                  Using the REPL, we can inspect the type of print-pict:

                                  > (#:type print-pict)
                                  +: (-> Pict (IO Unit))

                                  Unsurprisingly, displaying a picture to the screen needs IO. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of filled-square:

                                  > (#:type filled-square)
                                  +: (-> Double Pict)

                                  No IO to be seen! This is because “picts” are entirely pure values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:

                                  (main (print-pict {(colorize red (filled-square 50.0))
                                  +                   hc-append
                                  +                   (colorize blue (filled-square 50.0))}))

                                  This code will print out a red square to the left of a blue one.

                                  Again, hc-append is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:

                                  > (#:type hc-append)
                                  +: (-> Pict (-> Pict Pict))

                                  Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!

                                  Implementing a snake clone

                                  This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the pict or universe libraries. Hopefully it’s still illustrative.

                                  We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an Eq instance for our points.

                                  (data Point (point Integer Integer))
                                  +(data Direction d:left d:right d:up d:down)
                                  +
                                  +(instance (Eq Point)
                                  +  [== (λ [(point a b) (point c d)] {{a == c} && {b == d}})])

                                  With these two datatypes, we can implement a move function that accepts a point and a direction and produces a new point for an adjacent tile:

                                  (defn move : {Direction -> Point -> Point}
                                  +  [[d:left  (point x y)] (point {x - 1} y)]
                                  +  [[d:right (point x y)] (point {x + 1} y)]
                                  +  [[d:up    (point x y)] (point x {y - 1})]
                                  +  [[d:down  (point x y)] (point x {y + 1})])

                                  The next step is to define a type for our world state. The big-bang library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:

                                  (data World-State (world-state
                                  +                   Direction    ; snake direction
                                  +                   (List Point) ; snake blocks
                                  +                   (List Point) ; food blocks
                                  +                   ))

                                  It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:

                                  (defn set-ws-direction [[d (world-state a b c)] (world-state d b c)])

                                  Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:

                                  (def board-width 50)
                                  +(def board-height 30)
                                  +(def tile->absolute {(d* 15.0) . integer->double})
                                  +(def empty-board (blank-rect (tile->absolute board-width) (tile->absolute board-height)))
                                  +
                                  +(def block (filled-square 13.0))
                                  +(def food-block (colorize red block))
                                  +(def snake-block (colorize black block))

                                  Now we can write our actual render function. To do this, we simply need to render each Point in our World-State’s two lists as a block on an empty-board. We’ll write a helper function, render-on-board, which does exactly that:

                                  (defn render-on-board : {Pict -> (List Point) -> Pict}
                                  +  [[pict points]
                                  +   (foldr (λ [(point x y) acc]
                                  +            (pin-over acc (tile->absolute x) (tile->absolute y) pict))
                                  +          empty-board points)])

                                  This function uses foldr to collect each point and place the provided pict at the right location using pin-over on an empty board. Using render-on-board, we can write the render function in just a couple of lines:

                                  (defn render : {World-State -> Pict}
                                  +  [[(world-state _ snake-points food-points)]
                                  +   (pin-over (render-on-board snake-block snake-points)
                                  +             0.0 0.0
                                  +             (render-on-board food-block food-points))])

                                  Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the big-bang library provides a random-integer function, which we can use to write a random-point action:

                                  (def random-point : (IO Point)
                                  +  {point <$> (random-integer 0 board-width)
                                  +         <*> (random-integer 0 board-height)})

                                  Hackett supports applicative notation using infix operators, so random-point looks remarkably readable. It also runs in IO, since the result is, obviously, random. Fortunately, the on-tick function runs in IO as well (unlike render, which must be completely pure), so we can use random-point when necessary to generate a new food block:

                                  (def init! : (forall [a] {(List a) -> (List a)})
                                  +  {reverse . tail! . reverse})
                                  +
                                  +(defn on-tick : {World-State -> (IO World-State)}
                                  +  [[(world-state dir snake-points food-points)]
                                  +   (let ([new-snake-point (move dir (head! snake-points))])
                                  +     (if {new-snake-point elem? food-points}
                                  +         (do [new-food-point <- random-point]
                                  +             (pure (world-state dir {new-snake-point :: snake-points}
                                  +                                {new-food-point :: (delete new-snake-point food-points)})))
                                  +         (pure (world-state dir {new-snake-point :: (init! snake-points)}
                                  +                            food-points))))])

                                  This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to new-snake-point, then checks if there is a food block at that location. If there is, it generates a new-food-point, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.

                                  The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our set-ws-direction function that we wrote earlier:

                                  (defn on-key : {KeyEvent -> World-State -> (IO World-State)}
                                  +  [[ke:left ] {pure . (set-ws-direction d:left)}]
                                  +  [[ke:right] {pure . (set-ws-direction d:right)}]
                                  +  [[ke:up   ] {pure . (set-ws-direction d:up)}]
                                  +  [[ke:down ] {pure . (set-ws-direction d:down)}]
                                  +  [[_       ] {pure . id}])

                                  The on-key function runs in IO, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in pure.

                                  We’re almost done now—all we need to do is set up the initial state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:

                                  (def initial-state
                                  +  (do [initial-food <- (sequence (take 5 (repeat random-point)))]
                                  +      (pure (world-state d:right
                                  +                         {(point 25 15) :: (point 24 15) :: (point 23 15) :: nil}
                                  +                         initial-food))))

                                  Notably, we can use the repeat function to create an infinite list of random-point actions, take the first five of them, then call sequence to execute them from left to right. Now, all we have to do is put the pieces together in a main block:

                                  (main (do [state <- initial-state]
                                  +          (big-bang state
                                  +            #:to-draw render
                                  +            #:on-tick on-tick 0.2
                                  +            #:on-key on-key)))

                                  And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.

                                  Contributing to Hackett

                                  If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.

                                  On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, Hackett has an issue tracker, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.

                                  Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to open an issue on GitHub, send me a message on the #racket IRC channel on Freenode, or ping me on the Racket Slack team.

                                  Acknowledgements

                                  Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to Samuel Gélineau, aka gelisam, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.

                                  Additionally, I want to specially thank Matthew Flatt, Robby Findler, and Sam Tobin-Hochstadt for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.

                                  Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.

                                    \ No newline at end of file diff --git a/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/index.html b/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/index.html new file mode 100644 index 0000000..c90c354 --- /dev/null +++ b/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/index.html @@ -0,0 +1,119 @@ +A space of their own: adding a type namespace to Hackett

                                    A space of their own: adding a type namespace to Hackett

                                    As previously discussed on this blog, my programming language, Hackett, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?

                                    For now, at least, the answer is that Hackett will emulate Haskell: Hackett now has two namespaces. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.

                                    Why two namespaces?

                                    Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.

                                    This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:

                                    > (map first '((1 a) (2 b) (3 c)))
                                    +'(1 2 3)

                                    In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly annotate when they wish to use a value from a different namespace:

                                    > (mapcar #'car '((1 a) (2 b) (3 c)))
                                    +(1 2 3)

                                    The Common Lisp #'x reader abbreviation is equivalent to (function x), and function is a special form that references a value in the function namespace.

                                    While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.

                                    However, Hackett is a little different from all of the aforementioned languages because Hackett has types. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is always syntactically unambiguous.1 Even if types and values live in separate namespaces, there is no need for a type form a la CL’s function because it can always be determined implicitly.

                                    For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:

                                    (data (Tuple a b) (Tuple a b))

                                    This defines a binding named Tuple at the type level, which is a type constructor of two arguments that produces a type of kind *,2 and another binding named Tuple at the value level, which is a value constructor of two arguments that produces a value of type (Tuple a b).

                                    But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor tuple instead of Tuple? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:

                                    (data Foo bar (baz Integer))
                                    +
                                    +(defn foo->integer : {Foo -> Integer}
                                    +  [[bar    ] 0]
                                    +  [[(baz y)] y])

                                    This works fine. But what happens if the programmer decides to change the name of the bar value?

                                    (data Foo qux (baz Integer))
                                    +
                                    +(defn foo->integer : {Foo -> Integer}
                                    +  [[bar    ] 0]
                                    +  [[(baz y)] y])

                                    Can you spot the bug? Disturbingly, this code still compiles! Even though bar is not a member of Foo anymore, it’s still a valid pattern, since names used as patterns match anything, just as the y pattern matches against any integer inside the baz constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: (foo->integer (baz 42)) will still produce 0, not 42, since the first case always matches.

                                    Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the Tuple example above, which is illegal if a programming language only supports a single namespace.

                                    Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The Tuple example from above is perfectly legal Hackett.

                                    Adding namespaces to a language

                                    Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to implement such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?

                                    Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: the remainder of this blog post is highly technical, and some of it involves some of the more esoteric corners of Racket’s macro system. This blog post is not representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.

                                    Namespaces as scopes

                                    With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as sets of scopes, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.

                                    Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the value scope, and all identifiers in type position must have a different scope, which we will call the type scope. How do we create these scopes and apply them to identifiers? In Racket, we use a function called make-syntax-introducer, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can add the scope to all pieces of the syntax object, remove the scope, or flip the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call make-syntax-introducer once for each namespace:

                                    (begin-for-syntax
                                    +  (define value-introducer (make-syntax-introducer))
                                    +  (define type-introducer (make-syntax-introducer)))

                                    We define these in a begin-for-syntax block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:

                                    (require syntax/parse/define)
                                    +
                                    +(define-simple-macro (begin/value form ...)
                                    +  #:with [form* ...] (map (λ (stx) (value-introducer stx 'add))
                                    +                          (attribute form))
                                    +  (begin form* ...))
                                    +
                                    +(define-simple-macro (begin/type form ...)
                                    +  #:with [form* ...] (map (λ (stx) (type-introducer stx 'add))
                                    +                          (attribute form))
                                    +  (begin form* ...))

                                    Each of these two forms is like begin, which is a Racket form that is, for our purposes, essentially a no-op, but it applies value-introducer or type-introducer to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:

                                    (begin/value
                                    +  (define x 'value-x))
                                    +
                                    +(begin/type
                                    +  (define x 'type-x))
                                    +
                                    +(begin/value
                                    +  (println x))
                                    +
                                    +(begin/type
                                    +  (println x))

                                    This program produces the following output:

                                    'value-x
                                    +'type-x
                                    +

                                    It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.

                                    However, although this is close, it isn’t quite right. What happens if we nest the two inside each other?

                                    (begin/value
                                    +  (begin/type
                                    +    (println x)))
                                    x: identifier's binding is ambiguous
                                    +  context...:
                                    +   #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site)
                                    +   #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local)
                                    +   #(190359 intdef)
                                    +  matching binding...:
                                    +   #<module-path-index:()>
                                    +   #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site)
                                    +  matching binding...:
                                    +   #<module-path-index:()>
                                    +   #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site)
                                    +

                                    Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of x is ambiguous because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of begin/value or begin/type to override outer ones, ensuring that a use can only be in a single namespace at a time.

                                    To do this, we simply need to adjust begin/value and begin/type to remove the other scope in addition to adding the appropriate one:

                                    (define-simple-macro (begin/value form ...)
                                    +  #:with [form* ...] (map (λ (stx)
                                    +                            (type-introducer (value-introducer stx 'add) 'remove))
                                    +                          (attribute form))
                                    +  (begin form* ...))
                                    +
                                    +(define-simple-macro (begin/type form ...)
                                    +  #:with [form* ...] (map (λ (stx)
                                    +                            (value-introducer (type-introducer stx 'add) 'remove))
                                    +                          (attribute form))
                                    +  (begin form* ...))

                                    Now our nested program runs, and it produces 'type-x, which is exactly what we want—the “nearest” scope wins.

                                    With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.

                                    Namespaces that cross module boundaries

                                    The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.

                                    Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the other module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export symbols, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named foo, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the require form’s job to attach the correct scopes to imported identifiers.

                                    This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!3 This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary points of having multiple namespaces.

                                    What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s require and provide forms are extensible, it’s even possible to implement this mangling in a completely invisible way.

                                    Currently, the scheme that Hackett uses is to prefix #%hackett-type: onto the beginning of any type exports. This can be defined in terms of a provide pre-transformer, which is essentially a macro that cooperates with Racket’s provide form to control the export process. In this case, we can define our type-out provide pre-transformer in terms of prefix-out, a form built-in to Racket that allows prefixing the names of exports:

                                    (define-syntax type-out
                                    +  (make-provide-pre-transformer
                                    +   (λ (stx modes)
                                    +     (syntax-parse stx
                                    +       [(_ provide-spec ...)
                                    +        (pre-expand-export
                                    +         #`(prefix-out #%hackett-type:
                                    +                       #,(type-introducer
                                    +                          #'(combine-out provide-spec ...)))
                                    +         modes)]))))

                                    Note that we call type-introducer in this macro! That’s because we want to ensure that, when a user writes (provide (type-out Foo)), we look for Foo in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that provide knows which Foo is being provided.

                                    Once we have referenced the correct binding, the use of prefix-out will appropriately add the #%hackett-type: prefix, so the exporting side is already done. Users do need to explicitly write (type-out ....) if they are exporting a particular type-level binding, but this is rarely necessary, since most users use data or class to export datatypes or typeclasses respectively, which can be modified to use type-out internally. Very little user code actually needs to change to support this adjustment.

                                    Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the #%hackett-type: prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.

                                    Just as we extended provide with a provide pre-transformer, we can extend require using a require transformer. In code, this entire process looks like this:

                                    (begin-for-syntax
                                    +  (define (unmangle-type-name name)
                                    +    (and~> (regexp-match #rx"^#%hackett-type:(.+)$" name) second)))
                                    +
                                    +(define-syntax unmangle-types-in
                                    +  (make-require-transformer
                                    +   (syntax-parser
                                    +     [(_ require-spec ...)
                                    +      #:do [(define-values [imports sources]
                                    +              (expand-import #'(combine-in require-spec ...)))]
                                    +      (values
                                    +       (map (match-lambda
                                    +              [(and i (import local-id src-sym src-mod-path mode req-mode orig-mode orig-stx))
                                    +               (let* ([local-name (symbol->string (syntax-e local-id))]
                                    +                      [unmangled-type-name (unmangle-type-name local-name)])
                                    +                 (if unmangled-type-name
                                    +                     (let* ([unmangled-id
                                    +                             (datum->syntax local-id
                                    +                                            (string->symbol unmangled-type-name)
                                    +                                            local-id
                                    +                                            local-id)])
                                    +                       (import (type-introducer unmangled-id)
                                    +                               src-sym src-mod-path mode req-mode orig-mode orig-stx))
                                    +                     i))])
                                    +            imports)
                                    +       sources)])))

                                    This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:

                                    1. It iterates over each import and calls unmangle-type-name on the imported symbol. If the result is #f, that means the import does not have the #%hackett-type: prefix, and it can be safely passed through unchanged.

                                    2. If unmangle-type-name does not return #f, then it returns the unprefixed name, which is then provided to datum->syntax, which allows users to forge new identifiers in an unhygienic (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from unmangle-type-name, but with the lexical context of the original identifier.

                                    3. Finally, we pass the new identifier to type-introducer to properly add the type scope, injecting the fresh binding into the type namespace.

                                    With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write type-out when exporting types, it is unlikely that users will want to write unmangle-types-in around each and every import in their program. For that reason, we can define a slightly modified version of require that implicitly wraps all of its subforms with unmangle-types-in:

                                    (provide (rename-out [require/unmangle require]))
                                    +
                                    +(define-simple-macro (require/unmangle require-spec ...)
                                    +  (require (unmangle-types-in require-spec) ...))

                                    …and we’re done. Now, Hackett modules can properly import and export type-level bindings.

                                    Namespaces plus submodules: the devil’s in the details

                                    Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of submodules.

                                    Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.

                                    Submodules in Racket come in two flavors: module and module*. The difference is what order, semantically, they are defined in. Submodules defined with module are essentially defined before their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with module* are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.

                                    How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.

                                    However, there is a special sort of module* submodule that uses #f in place of a module language, which gives a module access to all of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a test submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:

                                    #lang racket
                                    +
                                    +; not provided
                                    +(define (private-add1 x)
                                    +  (+ x 1))
                                    +
                                    +(module* test #f
                                    +  (require rackunit)
                                    +  (check-equal? (private-add1 41) 42))

                                    It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:

                                    (begin-for-syntax
                                    +  (define value-introducer (make-syntax-introducer))
                                    +  (define type-introducer (make-syntax-introducer)))

                                    Remember that make-syntax-introducer is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module instantiation, as ensured by Racket’s separate compilation guarantee. This means that each module gets its own pair of scopes. This means the body of a module* submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.

                                    Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we can preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using make-syntax-delta-introducer, we can create a syntax introducer the adds or removes the difference between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for value-introducer and type-introducer to always operate on the same scopes on each module instantiation:

                                    (define-simple-macro (define-value/type-introducers
                                    +                       value-introducer:id type-introducer:id)
                                    +  #:with scopeless-id (datum->syntax #f 'introducer-id)
                                    +  #:with value-id ((make-syntax-introducer) #'scopeless-id)
                                    +  #:with type-id ((make-syntax-introducer) #'scopeless-id)
                                    +  (begin-for-syntax
                                    +    (define value-introducer
                                    +      (make-syntax-delta-introducer #'value-id #'scopeless-id))
                                    +    (define type-introducer
                                    +      (make-syntax-delta-introducer #'type-id #'scopeless-id))))
                                    +
                                    +(define-value/type-introducers value-introducer type-introducer)

                                    The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. However, code inside begin-for-syntax blocks is still re-evaluated every time the module is instantiated! This means we are not circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.

                                    We still use make-syntax-introducer to create our two scopes, but critically, we only call make-syntax-introducer inside the define-value/type-introducers macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds value-id and type-id as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use make-syntax-delta-introducer to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.

                                    By guaranteeing each namespace’s scope is always the same, even for different modules, module* submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.

                                    The final stretch: making Scribble documentation namespace-aware

                                    As discussed in my previous blog post, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.

                                    In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!

                                    Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.

                                    To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with t: upon import, and they are bound to Scribble element transformers that indicate they should be typeset without the t: prefix. Fortunately, Scribble’s documentation forms do understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.

                                    In practice, this means Hackett documentation must now include a proliferation of t: prefixes. For example, here is the code for a typeset REPL interaction:

                                    @(hackett-examples
                                    +  (defn square : (t:-> t:Integer t:Integer)
                                    +    [[x] {x * x}])
                                    +  (square 5))

                                    Note the use of t:-> and t:Integer instead of -> and Integer. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.

                                    This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides deftype and deftycon forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to t:-prefixed identifiers to properly index documented forms. Similarly, defdata and defclass have been updated with an understanding of types.

                                    The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of defform slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into t:-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.

                                    Analysis and unsolved problems

                                    When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging Matthew Flatt for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).

                                    For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom require form, types provided by a module’s language are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike require’s require transformers.

                                    To circumvent this restriction, #lang hackett’s reader includes a somewhat ad-hoc solution that actually inserts a require into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.

                                    That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do any special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully internalizes extra-linguistic mechanisms.

                                    As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:

                                    • Multi-parameter typeclasses are implemented, along with default typeclass method implementations.

                                    • Pattern-matching performs basic exhaustiveness checking, so unmatched cases are a compile-time error.

                                    • Hackett ships with a larger standard library, including an Either type and appropriate functions, an Identity type, a MonadTrans typeclass, and the ReaderT and ErrorT monad transformers.

                                    • More things are documented, and parts of the documentation are slightly improved. Additionally, Hackett’s internals are much more heavily commented, hopefully making the project more accessible to new contributors.

                                    • Parts of the typechecker are dramatically simplified, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.

                                    • As always, various bug fixes.

                                    Finally, special mention to two new contributors to Hackett, Milo Turner and Brendan Murphy. Also special thanks to Matthew Flatt and Michael Ballantyne for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.

                                    1. “But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have #lang. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate #lang, not a modified version of Hackett, so Hackett can optimize its user experience for what it is, not what it might be someday.

                                    2. Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow * to be used to mean “type” at the kind level and “multiply” at the value level.

                                    3. This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, per phase, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler.

                                    \ No newline at end of file diff --git a/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/index.html b/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/index.html new file mode 100644 index 0000000..4acd11c --- /dev/null +++ b/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/index.html @@ -0,0 +1,74 @@ +An opinionated guide to Haskell in 2018

                                    An opinionated guide to Haskell in 2018

                                    ⦿ haskell

                                    For me, this month marks the end of an era in my life: as of February 2018, I am no longer employed writing Haskell. It’s been a fascinating two years, and while I am excitedly looking forward to what I’ll be doing next, it’s likely I will continue to write Haskell in my spare time. I’ll probably even write it again professionally in the future.

                                    In the meantime, in the interest of both sharing with others the small amount of wisdom I’ve gained and preserving it for my future self, I’ve decided to write a long, rather dry overview of a few select parts of the Haskell workflow I developed and the ecosystem I settled into. This guide is, as the title notes, opinionated—it is what I used in my day-to-day work, nothing more—and I don’t claim that anything here is the only way to write Haskell, nor even the best way. It is merely what I found helpful and productive. Take from it as much or as little as you’d like.

                                    Build tools and how to use them

                                    When it comes to building Haskell, you have options. And frankly, most of them are pretty good. There was a time when cabal-install had a (warranted) reputation for being nearly impossible to use and regularly creating dependency hell, but I don’t think that’s the case anymore (though you do need to be a little careful about how you use it). Sandboxed builds work alright, and cabal new-build and the other cabal new-* commands are even better. That said, the UX of cabal-install is still less-than-stellar, and it has sharp edges, especially for someone coming from an ecosystem without a heavyweight compilation process like JavaScript, Ruby, or Python.

                                    Nix is an alternative way to manage Haskell dependencies, and it seems pretty cool. It has a reputation for being large and complicated, and that reputation does not seem especially unfair, but you get lots of benefits if you’re willing to pay the cost. Unfortunately, I have never used it (though I’ve read a lot about it), so I can’t comment much on it here. Perhaps I’ll try to go all-in with Nix when I purchase my next computer, but for now, my workflow works well enough that I don’t feel compelled to switch.

                                    Personally, I use stack as my Haskell build tool. It’s easy to use, it works out of the box, and while it doesn’t enjoy the same amount of caching as cabal new-build or Nix, it caches most packages, and it also makes things like git-hosted sources incredibly easy, which (as far as I can tell) can’t be done with cabal-install alone.

                                    This section is going to be a guide on how I use stack. If you use cabal-install with or without Nix, great! Those tools seem good, too. This is not an endorsement of stack over the other build tools, just a description of how I use it, the issues I ran into, and my solutions to them.

                                    Understanding stack’s model and avoiding its biggest gotcha

                                    Before using stack, there are a few things every programmer should know:

                                    • stack is not a package manager, it is a build tool. It does not manage a set of “installed” packages; it simply builds targets and their dependencies.

                                    • The command to build a target is stack build <target>. Just using stack build on its own will build the current project’s targets.

                                    • You almost certainly do not want to use stack install.

                                    This is the biggest point of confusion I see among new users of stack. After all, when you want to install a package with npm, you type npm install <package>. So a new Haskeller decides to install lens, types stack install lens, and then later tries stack uninstall lens, only to discover that no such command exists. What happened?

                                    stack install is not like npm install. stack install is like make install. It is nothing more than an alias for stack build --copy-bins, and all it does is build the target and copy all of its executables into some relatively global location like ~/.local/bin. This is usually not what you want.

                                    This design decision is not unique to stack; cabal-install suffers from it as well. One can argue that it isn’t unintuitive because it really is just following what make install conventionally does, and the fact that it happens to conflict with things like npm install or even apt-get install is just a naming clash. I think that argument is a poor one, however, and I think the decision to even include a stack install command was a bad idea.

                                    So, remember: don’t use stack install! stack works best when everything lives inside the current project’s local sandbox, and stack install copies executables into a global location by design. While it might sometimes appear to work, it’s almost always wrong. The only situation in which stack install is the right answer is when you want to install an executable for a use unrelated to Haskell development (that is, something like pandoc) that just so happens to be provided by a Haskell package. This means no running stack install ghc-mod or stack install intero either, no matter what READMEs might tell you! Don’t worry: I’ll cover the proper way to install those things later.

                                    Actually building your project with stack

                                    Okay, so now that you know to never use stack install, what do you use? Well, stack build is probably all you need. Let’s cover some variations of stack build that I use most frequently.

                                    Once you have a stack project, you can build it by simply running stack build within the project directory. However, for local development, this is usually unnecessarily slow because it runs the GHC optimizer. For faster development build times, pass the --fast flag to disable optimizations:

                                    $ stack build --fast
                                    +

                                    By default, stack builds dependencies with coarse-grained, package-level parallelism, but you can enable more fine-grained, module-level parallel builds by adding --ghc-options=-j. Unfortunately, there are conflicting accounts on whether or not this actually makes things faster or slower in practice, and I haven’t extensively tested to see whether or not this is the case, so I mostly leave it off.

                                    Usually, you also want to build and run the tests along with your code, which you can enable with the --test flag. Additionally, stack test is an alias for stack build --test, so these two commands are equivalent:

                                    $ stack build --fast --test
                                    +$ stack test --fast
                                    +

                                    Also, it is useful to build documentation as well as code! You can do this by passing the --haddock flag, but unfortunately, I find Haddock sometimes takes an unreasonably long time to run. Therefore, since I usually only care about running Haddock on my dependencies, I usually pass the --haddock-deps flag instead, which prevents having to re-run Haddock every time you build:

                                    $ stack test --fast --haddock-deps
                                    +

                                    Finally, I usually want to build and test my project in the background whenever my code changes. Fortunately, this can be done easily by using the --file-watch flag, making it easy to incrementally change project code and immediately see results:

                                    $ stack test --fast --haddock-deps --file-watch
                                    +

                                    This is the command I usually use to develop my Haskell projects.

                                    Accessing local documentation

                                    While Haskell does not always excel on the documentation front, a small amount of documentation is almost always better than no documentation at all, and I find my dependencies’ documentation to be an invaluable resource while developing. I find many people just look at docs on Hackage or use the hosted instance of Hoogle, but this sometimes leads people astray: they might end up looking at the wrong version of the documentation! Fortunately, there’s an easy solution to this problem, which is to browse the documentation stack installs locally, which is guaranteed to match the version you are using in your current project.

                                    The easiest way to open local documentation for a particular package is to use the stack haddock --open command. For example, to open the documentation for lens, you could use the following command:

                                    $ stack haddock --open lens
                                    +

                                    This will open the local documentation in your web browser, and you can browse it at your leisure. If you have already built the documentation using the --haddock-deps option I recommended in the previous section, this command should complete almost instantly, but if you haven’t built the documentation yet, you’ll have to wait as stack builds it for you on-demand.

                                    While this is a good start, it isn’t perfect. Ideally, I want to have searchable documentation, and fortunately, this is possible to do by running Hoogle locally. This is easy enough with modern versions of stack, which have built-in Hoogle integration, but it still requires a little bit of per-project setup, since you need to build the Hoogle search index with the following command:

                                    $ stack hoogle -- generate --local
                                    +

                                    This will install Hoogle into the current project if it isn’t already installed, and it will index your dependencies’ documentation and generate a new Hoogle database. Once you’ve done that, you can start a web server that serves a local Hoogle search page with the following command:

                                    $ stack hoogle -- server --local --port=8080
                                    +

                                    Navigate to http://localhost:8080 in your web browser, and you’ll have a fully-searchable index of all your Haskell packages’ documentation. Isn’t that neat?

                                    Unfortunately, you will have to manually regenerate the Hoogle database when you install new packages and their documentation, which you can do by re-running stack hoogle -- generate --local. Fortunately, regenerating the database doesn’t take very long, as long as you’ve been properly rebuilding the documentation with --haddock-deps.

                                    Configuring your project

                                    Every project built with stack is configured with two separate files:

                                    • The stack.yaml file, which controls which packages are built and what versions to pin your dependencies to.

                                    • The <project>.cabal file or package.yaml file, which specifies build targets, their dependencies, and which GHC options to apply, among other things.

                                    The .cabal file is, ultimately, what is used to build your project, but modern versions of stack generate projects that use hpack, which uses an alternate configuration file, the package.yaml file, to generate the .cabal file. This can get a little bit confusing, since it means you have three configuration files in your project, one of which is generated from the other one.

                                    I happen to use and like hpack, so I use a package.yaml file and allow hpack to generate the .cabal file. I have no real love for YAML, and in fact I think custom configuration formats are completely fine, but the primary advantage of hpack is the ability to specify things like GHC options and default language extensions for all targets at once, instead of needing to duplicate them per-target.

                                    You can think of the .cabal or package.yaml file as a specification for how your project is built and what packages it depends on, but the stack.yaml file is a specification of precisely which version of each package should be used and where it should be fetched from. Also, each .cabal file corresponds to precisely one Haskell package (though it may have any number of executable targets), but a stack.yaml file can specify multiple different packages to build, useful for multi-project builds that share a common library. The details here can be a little confusing, more than I am likely going to be able to explain in this blog post, but for the most part, you can get away with the defaults unless you’re doing something fancy.

                                    Setting up editor integration

                                    Currently, I use Atom to write Haskell. Atom is not a perfect editor by any means, and it leaves a lot to be desired, but it’s easy to set up, and the Haskell editor integration is decent.

                                    Atom’s editor integration is powered by ghc-mod, a program that uses the GHC API to provide tools to inspect Haskell programs. Installing ghc-mod must be done manually so that Atom’s haskell-ghc-mod package can find it, and this is where a lot of people get tripped up. They run stack install ghc-mod, it installs ghc-mod into ~/.local/bin, they put that in their PATH, and things work! …except when a new version of GHC is released a few months later, everything stops working.

                                    As mentioned above, stack install is not what you want. Tools like ghc-mod, hlint, hoogle, weeder, and intero work best when installed as part of the sandbox, not globally, since that ensures they will match the current GHC version your project is using. This can be done per-project using the ordinary stack build command, so the easiest way to properly install ghc-mod into a stack project is with the following command:

                                    $ stack build ghc-mod
                                    +

                                    Unfortunately, this means you will need to run that command inside every single stack project individually in order to properly set it up so that stack exec -- ghc-mod will find the correct executable. One way to circumvent this is by using a recently-added stack flag designed for this explicit purpose, --copy-compiler-tool. This is like --copy-bins, but it copies the executables into a compiler-specific location, so a tool built for GHC 8.0.2 will be stored separately from the same tool built for GHC 8.2.2. stack exec arranges for the executables for the current compiler version to end up in the PATH, so you only need to build and install your tools once per compiler version.

                                    Does this kind of suck? Yes, a little bit, but it sucks a whole lot less than all your editor integration breaking every time you switch to a project that uses a different version of GHC. I use the following command in a fresh sandbox when a Stackage LTS comes out for a new version of GHC:

                                    $ stack build --copy-compiler-tool ghc-mod hoogle weeder
                                    +

                                    This way, I only have to build those tools once, and I don’t worry about rebuilding them again until a the next release of GHC. To verify that things are working properly, you should be able to create a fresh stack project, run a command like this one, and get a similar result:

                                    $ stack exec -- which ghc-mod
                                    +/Users/alexis/.stack/compiler-tools/x86_64-osx/ghc-8.2.2/bin/ghc-mod
                                    +

                                    Note that this path is scoped to my operating system and my compiler version, but nothing else—no LTS or anything like that.

                                    Warning flags for a safe build

                                    Haskell is a relatively strict language as programming languages go, but in my experience, it isn’t quite strict enough. Many things are not errors that probably ought to be, like orphan instances and inexhaustive pattern matches. Fortunately, GHC provides warnings that catch these problems statically, which fill in the gaps. I recommend using the following flags on all projects to ensure everything is caught:

                                    The -Wall option turns on most warnings, but (ironically) not all of them. The -Weverything flag truly turns on all warnings, but some of the warnings left disabled by -Wall really are quite silly, like warning when type signatures on polymorphic local bindings are omitted. Some of them, however, are legitimately useful, so I recommend turning them on explicitly.

                                    -Wcompat enables warnings that make your code more robust in the face of future backwards-incompatible changes. These warnings are trivial to fix and serve as free future-proofing, so I see no reason not to turn these warnings on.

                                    -Wincomplete-record-updates and -Wincomplete-uni-patterns are things I think ought to be enabled by -Wall because they both catch what are essentially partial pattern-matches (and therefore runtime errors waiting to happen). The fact that -Wincomplete-uni-patterns isn’t enabled by -Wall is so surprising that it can lead to bugs being overlooked, since the extremely similar -Wincomplete-patterns is enabled by -Wall.

                                    -Wredundant-constraints is a useful warning that helps to eliminate unnecessary typeclass constraints on functions, which can sometimes occur if a constraint was previously necessary but ends up becoming redundant due to a change in the function’s behavior.

                                    I put all five of these flags in the .cabal file (or package.yaml), which enables them everywhere, but this alone is unlikely to enforce a warning-free codebase, since the build will still succeed even in the presence of warnings. Therefore, when building projects in CI, I pass the -Werror flag (using --ghc-options=-Werror for stack), which treats warnings as errors and halts the build if any warnings are found. This is useful, since it means warnings don’t halt the whole build while developing, making it possible to write some code that has warnings and still run the test suite, but it still enforces that pushed code be warning-free.

                                    Any flavor you like

                                    Haskell is both a language and a spectrum of languages. It is both a standard and a specific implementation. Haskell 98 and Haskell 2010 are good, small languages, and there are a few different implementations, but when people talk about “Haskell”, unqualified, they’re almost always talking about GHC.

                                    GHC Haskell, in stark contrast to standard Haskell, is neither small nor particularly specific, since GHC ships with dozens of knobs and switches that can be used to configure the language. In theory, this is a little terrifying. How could anyone ever hope to talk about Haskell and agree upon how to write it if there are so many different Haskells, each a little bit distinct? Having a cohesive ecosystem would be completely hopeless.

                                    Fortunately, in practice, this is not nearly as bad as it seems. The majority of GHC extensions are simple switches: a feature is either on or it is off. Turning a feature on rarely affects code that does not use it, so most extensions can be turned on by default, and programmers may simply avoid the features they do not wish to use, just as any programmer in any programming language likely picks a subset of their language’s features to use on a daily basis. Writing Haskell is not different in this regard, only in the sense that it does not allow all features to be used by default; everything from minor syntactic tweaks to entirely new facets of the type system are opt-in.

                                    Frankly, I think the UX around this is terrible. I recognize the desire to implement a standard Haskell, and the old -fglasgow-exts was not an especially elegant solution for people wishing to use nonstandard Haskell, but having to insert LANGUAGE pragmas at the top of every module just to take advantage of the best features GHC has to offer is a burden, and it is unnecessarily intimidating. I think much of the Haskell community finds the use of LANGUAGE pragmas preferable to enabling extensions globally using the default-extensions list in the .cabal file, but I cut across the grain on that issue hard. The vast majority of language extensions I use are extensions I want enabled all the time; a list of them at the top of a module is just distracting noise, and it only serves to bury the extensions I really do want to enable on a module-by-module basis. It also makes it tricky to communicate with a team which extensions are acceptable (or even preferable) and which are discouraged.

                                    My strong recommendation if you decide to write GHC Haskell on a team is to agree as a group to a list of extensions the team is happy with enabling everywhere and putting those extensions in the default-extensions list in the .cabal file. This eliminates clutter, busywork, and the conceptual overhead of remembering which extensions are in favor, and which are discouraged. This is a net win, and it isn’t at all difficult to look in the .cabal file when you want to know which extensions are in use.

                                    Now, with that small digression out of the way, the question becomes precisely which extensions should go into that default-extensions list. I happen to like using most of the features GHC makes available, so I enable a whopping 34 language extensions by default. As of GHC 8.2, here is my list:

                                    This is a lot, and a few of them are likely to be more controversial than others. Since I do not imagine everyone will agree with everything in this list, I’ve broken it down into smaller chunks, arranged from what I think ought to be least controversial to most controversial, along with a little bit of justification why each extension is in each category. If you’re interested in coming up with your own list of extensions, the rest of this section is for you.

                                    Trivial lifting of standards-imposed limitations

                                    A few extensions are tiny changes that lift limitations that really have no reason to exist, other than that they are mandated by the standard. I am not sure why these restrictions are in the standard to begin with, other than perhaps a misguided attempt at making the language simpler. These extensions include the following:

                                    These extensions have no business not being turned on everywhere. FlexibleContexts and FlexibleInstances end up being turned on in almost any nontrivial Haskell module, since without them, the typeclass system is pointlessly and artificially limited.

                                    InstanceSigs is extremely useful, completely safe, and has zero downsides.

                                    MultiParamTypeClasses are almost impossible to avoid, given how many libraries use them, and they are a completely obvious generalization of single-parameter typeclasses. Much like FlexibleContexts and FlexibleInstances, I see no real reason to ever leave these disabled.

                                    EmptyCase is even stranger to me, since EmptyDataDecls is in Haskell 2010, so it’s possible to define empty datatypes in standard Haskell but not exhaustively pattern-match on them! This is silly, and EmptyCase should be standard Haskell.

                                    Syntactic conveniences

                                    A few GHC extensions are little more than trivial, syntactic abbreviations. These things would be tiny macros in a Lisp, but they need to be extensions to the compiler in Haskell:

                                    All of these extensions are only triggered by explicit use of new syntax, so existing programs will never change behavior when these extensions are introduced.

                                    LambdaCase only saves a few characters, but it eliminates the need to come up with a fresh, unique variable name that will only be used once, which is sometimes hard to do and leads to worse names overall. Sometimes, it really is better to leave something unnamed.

                                    MultiWayIf isn’t something I find I commonly need, but when I do, it’s nice to have. It’s far easier to read than nested if...then...else chains, and it uses the existing guard syntax already used with function declarations and case...of, so it’s easy to understand, even to those unfamiliar with the extension.

                                    NamedFieldPuns avoids headaches and clutter when using Haskell records without the accidental identifier capture issues of RecordWildCards. It’s a nice, safe compromise that brings some of the benefits of RecordWildCards without any downsides.

                                    TupleSections is a logical generalization of tuple syntax in the same vein as standard operator sections, and it’s quite useful when using applicative notation. I don’t see any reason to not enable it.

                                    Extensions to the deriving mechanism

                                    GHC’s typeclass deriving mechanism is one of the things that makes Haskell so pleasant to write, and in fact I think Haskell would be nearly unpalatable to write without it. Boilerplate generation is a good thing, since it defines operations in terms of a single source of truth, and generated code is code you do not need to maintain. There is rarely any reason to write a typeclass instance by hand when the deriving mechanism will write it automatically.

                                    These extensions give GHC’s typeclass deriving mechanism more power without any cost. Therefore, I see no reason not to enable them:

                                    The first five of these simply extend the list of typeclasses GHC knows how to derive, something that will only ever be triggered if the user explicitly requests GHC derive one of those classes. GeneralizedNewtypeDeriving is quite possibly one of the most important extensions in all of Haskell, since it dramatically improves newtypes’ utility. Wrapper types can inherit instances they need without any boilerplate, and making increased type safety easier and more accessible is always a good thing in my book.

                                    DerivingStrategies is new to GHC 8.2, but it finally presents the functionality of GHC’s DeriveAnyClass extension in a useful way. DeriveAnyClass is useful when used with certain libraries that use DefaultSignatures (discussed later) with GHC.Generics to derive instances of classes without the deriving being baked into GHC. Unfortunately, enabling DeriveAnyClass essentially disables the far more useful GeneralizedNewtypeDeriving, so I do not recommend enabling DeriveAnyClass. Fortunately, with DerivingStrategies, it’s possible to opt into the anyclass deriving strategy on a case-by-case basis, getting some nice boilerplate reduction in the process.

                                    StandaloneDeriving is useful when GHC’s deriving algorithms aren’t quite clever enough to deduce the instance context automatically, so it allows specifying it manually. This is only useful in a few small situations, but it’s nice to have, and there are no downsides to enabling it, so it ought to be turned on.

                                    Lightweight syntactic adjustments

                                    A couple extensions tweak Haskell’s syntax in more substantial ways than things like LambdaCase, but not in a significant enough way for them to really be at all surprising:

                                    BangPatterns mirror strictness annotations on datatypes, so they are unlikely to be confusing, and they provide a much more pleasant notation for annotating the strictness of bindings than explicit uses of seq.

                                    KindSignatures are also fairly self-explanatory: they’re just like type annotations, but for types instead of values. Writing kind signatures explicitly is usually unnecessary, but they can be helpful for clarity or for annotating phantom types when PolyKinds is not enabled. Enabling KindSignatures doesn’t have any adverse effects, so I see no reason not to enable it everywhere.

                                    TypeOperators adjusts the syntax of types slightly, allowing operators to be used as type constructors and written infix, which is technically backwards-incompatible, but I’m a little suspicious of anyone using (!@#$) as a type variable (especially since standard Haskell does not allow them to be written infix). This extension is useful with some libraries like natural-transformations that provide infix type constructors, and it makes the type language more consistent with the value language.

                                    Polymorphic string literals

                                    I’m putting this extension in a category all of its own, mostly because I don’t think any other Haskell extensions have quite the same set of tradeoffs:

                                    For me, OverloadedStrings is not optional. Haskell’s infamous “string problem” (discussed in more detail at the end of this blog post) means that String is a linked list of characters, and all code that cares about performance actually uses Text. Manually invoking pack on every single string literal in a program is just noise, and OverloadedStrings solves that noise.

                                    That said, I actually find I don’t use the polymorphism of string literals very often, and I’d be alright with monomorphic literals if I could make them all have type Text. Unfortunately, there isn’t a way to do this, so OverloadedStrings is the next best thing, even if it sometimes causes some unnecessary ambiguities that require type annotations to resolve.

                                    OverloadedStrings is an extension that I use so frequently, in so many modules (especially in my test suites) that I would rather keep it on everywhere so I don’t have to care about whether or not it’s enabled in the module I’m currently writing. On the other hand, it certainly isn’t my favorite language extension, either. I wouldn’t go as far as to call it a necessary evil, since I don’t think it’s truly “evil”, but it does seem to be necessary.

                                    Simple extensions to aid type annotation

                                    The following two extensions significantly round out Haskell’s language for referring to types, making it much easier to insert type annotations where necessary (for removing ambiguity or for debugging type errors):

                                    That the behavior of ScopedTypeVariables is not the default is actually one of the most common gotchas for new Haskellers. Sadly, it can theoretically adjust the behavior of existing Haskell programs, so I cannot include it in the list of trivial changes, but I would argue such programs were probably confusing to begin with, and I have never seen a program in practice that was impacted by that problem. I think leaving ScopedTypeVariables off is much, much more likely to be confusing than turning it on.

                                    TypeApplications is largely unrelated, but I include it in this category because it’s quite useful and cooperates well with ScopedTypeVariables. Use of TypeApplications makes instantiation much more lightweight than full-blown type annotations, and once again, it has no downsides if it is enabled and unused (since it is a syntactic addition). I recommend enabling it.

                                    Simple extensions to the Haskell type system

                                    A few extensions tweak the Haskell type system in ways that I think are simple enough to be self-explanatory, even to people who might not have known they existed. These are as follows:

                                    ConstraintKinds is largely just used to define typeclass aliases, which is both useful and self-explanatory. Unifying the type and constraint language also has the effect of allowing type-level programming with constraints, which is sometimes useful, but far rarer in practice than the aforementioned use case.

                                    RankNTypes are uncommon, looking at the average type in a Haskell program, but they’re certainly nice to have when you need them. The idea of pushing foralls further into a type to adjust how variables are quantified is something that I find people find fairly intuitive, especially after seeing them used once or twice, and higher-rank types do crop up regularly, if infrequently.

                                    Intermediate syntactic adjustments

                                    Three syntactic extensions to Haskell are a little bit more advanced than the ones I’ve already covered, and none of them are especially related:

                                    ApplicativeDo is, on the surface, simple. It changes do notation to use Applicative operations where possible, which allows using do notation with applicative functors that are not monads, and it also makes operations potentially more performant when (<*>) can be implemented more efficiently than (>>=). In theory, it sounds like there are no downsides to enabling this everywhere. However, there are are a few drawbacks that lead me to put it so low on this list:

                                    1. It considerably complicates the desugaring of do blocks, to the point where the algorithm cannot even be easily syntactically documented. In fact, an additional compiler flag, -foptimal-applicative-do, is a way to opt into optimal solutions for do block expansions, tweaking the desugaring algorithm to have an O(n3) time complexity! This means that the default behavior is guided by a heuristic, and desugaring isn’t even especially predictable. This isn’t necessarily so bad, since it’s really only intended as an optimization when some Monad operations are still necessary, but it does dramatically increase the complexity of one of Haskell’s core forms.

                                    2. The desugaring, despite being O(n2) by default, isn’t even especially clever. It relies on a rather disgusting hack that recognizes return e, return $ e, pure e, or pure $ e expressions syntactically, and it completely gives up if an expression with precisely that shape is not the final statement in a do block. This is a bit awkward, since it effectively turns return and pure into syntax when before they were merely functions, but that isn’t all. It also means that the following do block is not desugared using Applicative operations:

                                      do foo a b
                                      +   bar s t
                                      +   baz y z

                                      This will use the normal, monadic desugaring, despite the fact that it is trivially desugared into Applicative operations as foo a b *> bar s t *> baz y z. In order to get ApplicativeDo to trigger here, the do block must be contorted into the following:

                                      do foo a b
                                      +   bar s t
                                      +   r <- baz y z
                                      +   pure r

                                      This seems like an odd oversight.

                                    3. TemplateHaskell doesn’t seem able to cope with do blocks when ApplicativeDo is enabled. I reported this as an issue on the GHC bug tracker, but it hasn’t received any attention, so it’s not likely to get fixed unless someone takes the initiative to do so.

                                    4. Enabling ApplicativeDo can cause problems with code that may have assumed do would always be monadic, and sometimes, that can cause code that typechecks to lead to an infinite loop at runtime. Specifically, if do notation is used to define (<*>) in terms of (>>=), enabling ApplicativeDo will cause the definition of (<*>) to become self-referential and therefore divergent. Fortunately, this issue can be easily mitigated by simply writing (<*>) = ap instead, which is clearer and shorter than the equivalent code using do.

                                    Given all these things, it seems ApplicativeDo is a little too new in a few places, and it isn’t quite baked. Still, I keep it enabled by default. Why? Well, usually it works fine without any problems, and when I run into issues, I can disable it on a per-module basis by writing {-# LANGUAGE NoApplicativeDo #-}. I still find that keeping it enabled by default is fine the vast majority of the time, I just sometimes need to work around the bugs.

                                    In contrast, DefaultSignatures isn’t buggy at all, as far as I can tell, it’s just not usually useful without fairly advanced features like GADTs (for type equalities) or GHC.Generics. I mostly use it for making lifting instances for mtl-style typeclasses easier to write, which I’ve found to be a tiny bit tricky to explain (mostly due to the use of type equalities in the context), but it works well. I don’t see any real reason to leave this disabled, but if you don’t think you’re going to use it anyway, it doesn’t really matter one way or the other.

                                    Finally, PatternSynonyms allow users to extend the pattern language just as they are allowed to extend the value language. Bidirectional pattern synonyms are isomorphisms, and it’s quite useful to allow those isomorphisms to be used with Haskell’s usual pattern-matching syntax. I think this extension is actually quite benign, but I put it so low on this list because it seems infrequently used, and I get the sense most people consider it fairly advanced. I would argue, however, that it’s a very pleasant, useful extension, and it’s no more complicated than a number of the features in Haskell 98.

                                    Intermediate extensions to the Haskell type system

                                    Now we’re getting into the meat of things. Everything up to this point has been, in my opinion, completely self-evident in its usefulness and simplicity. As far as I’m concerned, the extensions in the previous six sections have no business ever being left disabled. Starting in this section, however, I could imagine a valid argument being made either way.

                                    The following three extensions add some complexity to the Haskell type system in return for some added expressive power:

                                    ExistentialQuantification and GADTs are related, given that the former is subsumed by the latter, but GADTs also enables an alternative syntax. Both syntaxes allow packing away a typeclass dictionary or equality constraint that is brought into scope upon a successful pattern-match against a data constructor, something that is sometimes quite useful but certainly a departure from Haskell’s simple ADTs.

                                    FunctionalDependencies extend multi-parameter typeclasses, and they are almost unavoidable, given their use in the venerable mtl library. Like GADTs, FunctionalDependencies add an additional layer of complexity to the typeclass system in order to express certain things that would otherwise be difficult or impossible.

                                    All of these extensions involve a tradeoff. Enabling GADTs also implies MonoLocalBinds, which disables let generalization, one of the most likely ways a program that used to typecheck might subsequently fail to do so. Some might argue that this is a good reason to turn GADTs on in a per-module basis, but I disagree: I actually want my language to be fairly consistent, and given that I know I am likely going to want to use GADTs somewhere, I want MonoLocalBinds enabled everywhere, not inconsistently and sporadically.

                                    That aside, all these extensions are relatively safe. They are well-understood, and they are fairly self-contained extensions to the Haskell type system. I think these extensions have a very good power to cost ratio, and I find myself using them regularly (especially FunctionalDependencies), so I keep them enabled globally.

                                    Advanced extensions to the Haskell type system

                                    Finally, we arrive at the last set of extensions in this list. These are the most advanced features Haskell’s type system currently has to offer, and they are likely to be the most controversial to enable globally:

                                    All of these extensions exist exclusively for the purpose of type-level programming. DataKinds allows datatype promotion, creating types that are always uninhabited and therefore can only be used phantom. TypeFamilies allows the definition of type-level functions that map types to other types. Both of these are minor extensions to Haskell’s surface area, but they have rather significant ramifications on the sort of programming that can be done and the way GHC’s typechecker must operate.

                                    TypeFamilies is an interesting extension because it comes in so many flavors: associated type synonyms, associated datatypes, open and closed type synonym families, and open and closed datatype families. Associated types tend to be easier to grok and easier to use, though they can also be replaced by functional dependencies. Open type families are also quite similar to classes and instances, so they aren’t too tricky to understand. Closed type families, on the other hand, are a rather different beast, and they can be used to do fairly advanced things, especially in combination with DataKinds.

                                    I happen to appreciate GHC’s support for these features, and while I’m hopeful that an eventual DependentHaskell will alleviate many of the existing infelicities with dependently typed programming in GHC, in the meantime, it’s often useful to enjoy what exists where practically applicable. Therefore, I have little problem keeping them enabled, since, like the vast majority of extensions on this list, these extensions merely lift restrictions, not adjust semantics of the language without the extensions enabled. When I am going to write a type family, I am going to turn on TypeFamilies; I see no reason to annotate the modules in which I decide to do so. I do not write an annotation at the top of each module in which I define a typeclass or a datatype, so why should I do so with type families?

                                    TypeFamilyDependencies is a little bit different, since it’s a very new extension, and it doesn’t seem to always work as well as I would hope. Still, when it doesn’t work, it fails with a very straightforward error message, and when it works, it is legitimately useful, so I don’t see any real reason to leave it off if TypeFamilies is enabled.

                                    Extensions intentionally left off this list

                                    Given what I’ve said so far, it may seem like I would advocate flipping on absolutely every lever GHC has to offer, but that isn’t actually true. There are a few extensions I quite intentionally do not enable.

                                    UndecidableInstances is something I turn on semi-frequently, since GHC’s termination heuristic is not terribly advanced, but I turn it on per-module, since it’s useful to know when it’s necessary (and in application code, it rarely is). OverlappingInstances and IncoherentInstances, in contrast, are completely banned—not only are they almost always a bad idea, GHC has a better, more fine-grained way to opt into overlapping instances, using the {-# OVERLAPPING #-}, {-# OVERLAPPABLE #-}, and {-# INCOHERENT #-} pragmas.

                                    TemplateHaskell and QuasiQuotes are tricky ones. Anecdotes seem to suggest that enabling TemplateHaskell everywhere leads to worse compile times, but after trying this on a few projects and measuring, I wasn’t able to detect any meaningful difference. Unless I manage to come up with some evidence that these extensions actually slow down compile times just by being enabled, even if they aren’t used, then I may add them to my list of globally-enabled extensions, since I use them regularly.

                                    Other extensions I haven’t mentioned are probably things I just don’t use very often and therefore haven’t felt the need to include on this list. It certainly isn’t exhaustive, and I add to it all the time, so I expect I will continue to do so in the future. This is just what I have for now, and if your favorite extension isn’t included, it probably isn’t a negative judgement against that extension. I just didn’t think to mention it.

                                    Libraries: a field guide

                                    Now that you’re able to build a Haskell project and have chosen which handpicked flavor of Haskell you are going to write, it’s time to decide which libraries to use. Haskell is an expressive programming language, and the degree to which different libraries can shape the way you structure your code is significant. Picking the right libraries can lead to clean code that’s easy to understand and maintain, but picking the wrong ones can lead to disaster.

                                    Of course, there are thousands of Haskell libraries on Hackage alone, so I cannot hope to cover all of the ones I have ever found useful, and I certainly cannot cover ones that would be useful but I did not have the opportunity to try (of which there are certainly many). This blog post is long enough already, so I’ll just cover a few categories of libraries that I think I can offer interesting commentary on; most libraries can generally speak for themselves.

                                    Having an effect

                                    One of the first questions Haskell programmers bump into when they begin working on a large application is how they’re going to model effects. Few practical programming languages are pure, but Haskell is one of them, so there’s no getting away from coming up with a way to manage side-effects.

                                    For some applications, Haskell’s built-in solution might be enough: IO. This can work decently for data processing programs that do very minimal amounts of I/O, and the types of side-effects they perform are minimal. For these applications, most of the logic is likely to be pure, which means it’s already easy to reason about and easy to test. For other things, like web applications, it’s more likely that a majority of the program logic is going to be side-effectful by its nature—it may involve making HTTP requests to other services, interacting with a database, and writing to logfiles.

                                    Figuring out how to structure these effects in a type-safe, decoupled, composable way can be tricky, especially since Haskell has so many different solutions. I could not bring myself to choose just one, but I did choose two: the so-called “mtl style” and freer monads.

                                    mtl style is so named because it is inspired by the technique of interlocking monadic typeclasses and lifting instances used to model effects using constraints that is used in the mtl library. Here is a small code example of what mtl style typeclasses and handlers look like:

                                    class Monad m => MonadFileSystem m where
                                    +  readFile :: FilePath -> m String
                                    +  writeFile :: FilePath -> String -> m ()
                                    +
                                    +  default readFile :: (MonadTrans t, MonadFileSystem m', m ~ t m') => FilePath -> m String
                                    +  readFile a = lift $ readFile a
                                    +
                                    +  default writeFile :: (MonadTrans t, MonadFileSystem m', m ~ t m') => FilePath -> String -> m ()
                                    +  writeFile a b = lift $ writeFile a b
                                    +
                                    +instance MonadFileSystem IO where
                                    +  readFile = Prelude.readFile
                                    +  writeFile = Prelude.writeFile
                                    +
                                    +instance MonadFileSystem m => MonadFileSystem (ExceptT e m)
                                    +instance MonadFileSystem m => MonadFileSystem (MaybeT m)
                                    +instance MonadFileSystem m => MonadFileSystem (ReaderT r m)
                                    +instance MonadFileSystem m => MonadFileSystem (StateT s m)
                                    +instance MonadFileSystem m => MonadFileSystem (WriterT w m)
                                    +
                                    +newtype InMemoryFileSystemT m a = InMemoryFileSystemT (StateT [(FilePath, String)] m a)
                                    +  deriving (Functor, Applicative, Monad, MonadError e, MonadReader r, MonadWriter w)
                                    +
                                    +instance Monad m => MonadFileSystem (InMemoryFileSystemT m) where
                                    +  readFile path = InMemoryFileSystemT $ do
                                    +    vfs <- get
                                    +    case lookup path vfs of
                                    +      Just contents -> pure contents
                                    +      Nothing -> error ("readFile: no such file " ++ path)
                                    +
                                    +  writeFile path contents = InMemoryFileSystemT $ modify $ \vfs ->
                                    +    (path, contents) : delete (path, contents) vfs

                                    This is the most prevalent way to abstract over effects in Haskell, and it’s been around for a long time. Due to the way it uses the typeclass system, it’s also very fast, since GHC can often specialize and inline the typeclass dictionaries to avoid runtime dictionary passing. The main drawbacks are the amount of boilerplate required and the conceptual difficulty of understanding exactly how monad transformers, monadic typeclasses, and lifting instances all work together to discharge mtl style constraints.

                                    There are various alternatives to mtl’s direct approach to effect composition, most of which are built around the idea of reifying a computation as a data structure and subsequently interpreting it. The most popular of these is the Free monad, a clever technique for deriving a monad from a functor that happens to be useful for modeling programs. Personally, I think Free is overhyped. It’s a cute, mathematically elegant technique, but it involves a lot of boilerplate, and composing effect algebras is still a laborious process. The additional expressive power of Free, namely its ability to choose an interpreter dynamically, at runtime, is rarely necessary or useful, and it adds complexity and reduces performance for few benefits. (And in fact, this is still possible to do with mtl style, it’s just uncommon because there is rarely any need to do so.)

                                    A 2017 blog post entitled Free monad considered harmful discussed Free in comparison with mtl style, and unsurprisingly cast Free in a rather unflattering light. I largely agree with everything outlined in that blog post, so I will not retread its arguments here. I do, however, think that there is another abstraction that is quite useful: the so-called “freer monad” used to implement extensible effects.

                                    Freer moves even further away from worrying about functors and monads, since its effect algebras do not even need to be functors. Instead, freer’s effect algebras are ordinary GADTs, and reusable, composable effect handlers are easily written to consume elements of these datatypes. Unfortunately, the way this works means that GHC is still not clever enough to optimize freer monads as efficiently as mtl style, since it can’t easily detect when the interpreter is chosen statically and use that information to specialize and inline effect implementations, but the cost difference is significantly reduced, and I’ve found that in real application code, the vast majority of the cost does not come from the extra overhead introduced by a more expensive (>>=).

                                    There are a few different implementations of freer monads, but I, sadly, was not satisfied with any of them, so I decided to contribute to the problem by creating yet another one. My implementation is called freer-simple, and it includes a streamlined API with more documentation than any other freer implementation. Writing the above mtl style example using freer-simple is more straightforward:

                                    data FileSystem r where
                                    +  ReadFile :: FilePath -> FileSystem String
                                    +  WriteFile :: FilePath -> String -> FileSystem ()
                                    +
                                    +readFile :: Member FileSystem r => FilePath -> Eff r String
                                    +readFile a = send $ ReadFile a
                                    +
                                    +writeFile :: Member FileSystem r => FilePath -> String -> Eff r ()
                                    +writeFile a b = send $ WriteFile a b
                                    +
                                    +runFileSystemIO :: LastMember IO r => Eff (FileSystem ': r) ~> Eff r
                                    +runFileSystemIO = interpretM $ \case
                                    +  ReadFile a -> Prelude.readFile a
                                    +  WriteFile a b -> Prelude.writeFile a b
                                    +
                                    +runFileSystemInMemory :: [(FilePath, String)] -> Eff (FileSystem ': effs) ~> Eff effs
                                    +runFileSystemInMemory initVfs = runState initVfs . fsToState where
                                    +  fsToState :: Eff (FileSystem ': effs) ~> Eff (State [(FilePath, String)] ': effs)
                                    +  fsToState = reinterpret $ case
                                    +    ReadFile path -> get >>= \vfs -> case lookup path vfs of
                                    +      Just contents -> pure contents
                                    +      Nothing -> error ("readFile: no such file " ++ path)
                                    +    WriteFile path contents -> modify $ \vfs ->
                                    +      (path, contents) : delete (path, contents) vfs

                                    (It could be simplified further with a little bit of Template Haskell to generate the readFile and writeFile function definitions, but I haven’t gotten around to writing that.)

                                    So which effect system do I recommend? I used to recommend mtl style, but as of only two months ago, I now recommend freer-simple. It’s easier to understand, involves less boilerplate, achieves “good enough” performance, and generally gets out of the way wherever possible. Its API is designed to make it easy to do the sorts of the things you most commonly need to do, and it provides a core set of effects that can be used to build a real-world application.

                                    That said, freer is indisputably relatively new and relatively untested. It has success stories, but mtl style is still the approach used by the majority of the ecosystem. mtl style has more library support, its performance characteristics are better understood, and it is a tried and true way to structure effects in a Haskell application. If you understand it well enough to use it, and you are happy with it in your application, my recommendation is to stick with it. If you find it confusing, however, or you end up running up against its limits, give freer-simple a try.

                                    Through the looking glass: to lens or not to lens

                                    There’s no getting around it: lens is a behemoth of a library. For a long time, I wrote Haskell without it, and honestly, it worked out alright. I just wasn’t doing a whole lot of work that involved complicated, deeply-nested data structures, and I didn’t feel the need to bring in a library with such a reputation for having impenetrable operators and an almost equally impenetrable learning curve.

                                    But, after some time, I decided I wanted to take the plunge. So I braced myself for the worst, pulled out my notebook, and started writing some code. To my surprise… it wasn’t that hard. It made sense. Sure, I still don’t know how it works on the inside, and I never did learn the majority of the exports in Control.Lens.Operators, but I had no need to. Lenses were useful in the way I had expected them to be, and so were prisms. One thing led to another, and before long, I understood the relationship between the various optics, the most notable additions to my toolkit being folds and traversals. Sure, the type errors were completely opaque much of the time, but I was able to piece things together with ample type annotations and time spent staring at ill-typed expressions. Before long, I had developed an intuition for lens.

                                    After using it for a while, I retrospected on whether or not I liked it, and honestly, I still can’t decide. Some lensy expressions were straightforward to read and were a pleasant simplification, like this one:

                                    paramSpecs ^.. folded._Required

                                    Others were less obviously improvements, such as this beauty:

                                    M.fromList $ paramSpecs ^.. folded._Optional.filtered (has $ _2._UsePreviousValue)

                                    But operator soup aside, there was something deeper about lens that bothered me, and I just wasn’t sure what. I didn’t know how to articulate my vague feelings until I read a 2014 blog post entitled Lens is unidiomatic Haskell, which includes a point that I think is spot-on:

                                    Usually, types in Haskell are rigid. This leads to a distinctive style of composing programs: look at the types and see what fits where. This is impossible with lens, which takes overloading to the level mainstream Haskell probably hasn’t seen before.

                                    We have to learn the new language of the lens combinators and how to compose them, instead of enjoying our knowledge of how to compose Haskell functions. Formally, lens types are Haskell function types, but while with ordinary Haskell functions you immediately see from types whether they can be composed, with lens functions this is very hard in practice.

                                    […]

                                    Now let me clarify that this doesn’t necessarily mean that lens is a bad library. It’s an unusual library. It’s almost a separate language, with its own idioms, embedded in Haskell.

                                    The way lens structures its types deliberately introduces a sort of subtyping relationship—for example, all lenses are traversals and all traversals are folds, but not vice versa—and indeed, knowing this subtyping relationship is essential to working with the library and understanding how to use it. It is helpfully documented with a large diagram on the lens package overview page, and that diagram was most definitely an invaluable resource for me when I was learning how to use the library.

                                    On the surface, this isn’t unreasonable. Subtyping is an enormously useful concept! The only reason Haskell dispenses with it entirely is because it makes type inference notoriously difficult. The subtyping relation between optics is one of the things that makes them so useful, since it allows you to easily compose a lens with a prism and get a traversal out. Unfortunately, the downside of all this is that Haskell does not truly have subtyping, so all of lens’s “types” really must be type aliases for types of roughly the same shape, namely functions. This makes type errors completely baffling, since the errors do not mention the aliases, only the fully-expanded types (which are often rather complicated, and their meaning is not especially clear without knowing how lens works under the hood).

                                    So the above quote is correct: working with lens really is like working in a separate embedded language, but I’m usually okay with that. Embedded, domain-specific languages are good! Unfortunately, in this case, the host language is not very courteous to its guest. Haskell does not appear to be a powerful enough language for lens to be a language in its own right, so it must piggyback on top of Haskell’s error reporting mechanisms, which are insufficient for lens to be a cohesive linguistic abstraction. Just as debugging code by stepping through the assembly it produces (or, perhaps more relevant in 2018, debugging a compile-to-JS language by looking at the emitted JavaScript instead of the source code) makes for an unacceptably leaky language. We would never stand for such a thing in our general-purpose language tooling, and we should demand better even in our embedded languages.

                                    That said, lens is just too useful to ignore. It is a hopelessly leaky abstraction, but it’s still an abstraction, and a powerful one at that. Given my selection of default extensions as evidence, I think it’s clear I have zero qualms with “advanced” Haskell; I will happily use even singletons where it makes sense. Haskell’s various language extensions are sometimes confusing in their own right, but their complexity is usually fundamental to the expressive power they bring. lens has some fundamental complexity, too, but it is mostly difficult for the wrong reasons. Still, while it is not the first library I reach for on every new Haskell project, manipulating nested data without lens is just too unpleasant after tasting the nectar, so I can’t advise against it in good faith.

                                    Sadly, this means I’m a bit wishy-washy when it comes to using lens, but I do have at least one recommendation: if you decide to use lens, it’s better to go all-in. Don’t generate lenses for just a handful of datatypes, do it for all of them. You can definitely stick to a subset of the lens library’s features, but don’t apply it in some functions but not others. Having too many different, equally valid ways of doing things leads to confusion and inconsistency, and inconsistency minimizes code reuse and leads to duplication and spaghetti. Commit to using lens, or don’t use it at all.

                                    Mitigating the string problem

                                    Finally, Haskell has a problem with strings. Namely, String is a type alias for [Char], a lazy, singly linked list of characters, which is an awful representation of text. Fortunately, the answer to this problem is simple: ban String in your programs.

                                    Use Text everywhere. I don’t really care if you pick strict Text or lazy Text, but pick one and stick to it. Don’t ever use String, and especially don’t ever, ever, ever use ByteString to represent text! There are enormously few legitimate cases for using ByteString in a program that is not explicitly about reading or writing raw data, and even at that level, ByteString should only be used at program boundaries. In that sense, I treat ByteString much the same way I treat IO: push it to the boundaries of your program.

                                    One of Haskell’s core tenets is making illegal states unrepresentable. Strings are not especially useful datatypes for this, since they are sequences of arbitrary length made up of atoms that can be an enormously large number of different things. Still, string types enforce a very useful invariant, a notion of a sequence of human-readable characters. In the presence of Unicode, this is a more valuable abstraction than it might seem, and the days of treating strings as little different from sequences of bytes are over. While strings make a poor replacement for enums, they are quite effective at representing the incredible amount of text humans produce in a staggeringly large number of languages, and they are the right type for that job.

                                    ByteString, on the other hand, is essentially never the right type for any job. If a type classifies a set of values, ByteString is no different from Any. It is the structureless type, the all-encompassing blob of bits. A ByteString could hold anything at all—some text, an image, an executable program—and the type system certainly isn’t going to help to answer that question. The only use case I can possibly imagine for passing around a ByteString in your program rather than decoding it into a more precise type is if it truly holds opaque data, e.g. some sort of token or key provided by a third party with no structure guaranteed whatsoever. Still, even this should be wrapped in a newtype so that the type system enforces this opaqueness.

                                    Troublingly, ByteString shows up in many libraries’ APIs where it has no business being. In many cases, this seems to be things where ASCII text is expected, but this is hardly a good reason to willingly accept absolutely anything and everything! Make an ASCII type that forbids non-ASCII characters, and provide a ByteString -> Maybe ASCII function. Alternatively, think harder about your problem in question to properly support Unicode as you almost certainly ought to.

                                    Other places ByteString appears are similarly unfortunate. Base-64 encoding, for example, could be given the wonderfully illustrative type ByteString -> Text, or even ByteString -> ASCII! Such a type makes it immediately clear why base-64 is useful: it allows transforming arbitrary binary data into a reliable textual encoding. If we consider that ByteString is essentially Any, this function has the type Any -> ASCII, which is amazingly powerful! We can convert anything to ASCII text!

                                    Existing libraries, however, just provide the boring, disappointingly inaccurate type ByteString -> ByteString, which is one of the most useless types there is. It is essentially Any -> Any, the meaningless function type. It conveys nothing about what it does, other than that it is pure. Giving a function this type is scarcely better than dynamic typing. Its mere existence is a failure of Haskell library design.

                                    But wait, it gets worse! Data.Text.Encoding exports a function called decodeUtf8, which has type ByteString -> Text. What an incredible function with a captivating type! Whatever could it possibly do? Again, this function’s type is basically Any -> Text, which is remarkable in the power it gives us. Let’s try it out, shall we?

                                    ghci> decodeUtf8 "\xc3\x28"
                                    +"*** Exception: Cannot decode byte '\x28': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream
                                    +

                                    Oh. Well, that’s a disappointment.

                                    Haskell’s string problem goes deeper than String versus Text; it seems to have wound its way around the collective consciousness of the Haskell community and made it temporarily forget that it cares about types and totality. This isn’t that hard, I swear! I can only express complete befuddlement at how many of these APIs are just completely worthless.

                                    Fortunately, there is a way out, and that way out is text-conversions. It is the first Haskell library I ever wrote. It provides type safe, total conversions between Text and various other types, and it is encoding aware. It provides appropriately-typed base-16 and base-64 conversion functions, and is guaranteed to never raise any exceptions. Use it, and apply the Haskell philosophy to your strings, just as you already do for everything else in your program.

                                    Closing thoughts

                                    Phew.

                                    When I started writing this blog post, it used the phrase “short overview” in the introduction. It is now over ten thousand words long. I think that’s all I have it in me to say for now.

                                    Haskell is a wonderful language built by a remarkable group of people. Its community is often fraught with needlessly inflammatory debates about things like the value of codes of conduct, the evils of Hackage revisions, and precisely how much or how little people ought to care about the monad laws. These flame wars frustrate me to no end, and they sometimes go so far as to make me ashamed to call myself a part of the Haskell community. Many on the “outside” seem to view Haskellers as an elitist, mean-spirited cult, more interested in creating problems for itself than solving them.

                                    That perception is categorically wrong.

                                    I have never been in a community of programmers so dedicated and passionate about applying thought and rigor to building software, then going out and actually doing it. I don’t know anywhere else where a cutting-edge paper on effect systems is discussed by the very same people who are figuring out how to reliably deploy distributed services to AWS. Some people view the Haskell community as masturbatory, and to some extent, they are probably right. One of my primary motivators for writing Haskell is that it is fun and it challenges me intellectually in ways that other languages don’t. But that challenge is not a sign of uselessness, it is a sign that Haskell is so close to letting me do the right thing, to solving the problem the right way, to letting me work without compromises. When I write in most programming languages, I must constantly accept that my program will never be robust in all the ways I want it to be, and I might as well give up before I even start. Haskell’s greatest weakness is that it tempts me to try.

                                    Haskell is imperfect, as it will always be. I doubt I will ever be satisfied by any language or any ecosystem. There will always be more to learn, more to discover, better tools and abstractions to develop. Many of them will not look anything like Haskell; they may not involve formal verification or static types or effect systems at all. Perhaps live programming, structural editors, and runtime hotswapping will finally take over the world, and we will find that the problems we thought we were solving were irrelevant to begin with. I can’t predict the future, and while I’ve found great value in the Haskell school of program construction, I dearly hope that we do not develop such tunnel vision that we cannot see that there may be other ways to solve these problems. Many of the solutions are things we likely have not even begun to think about. Still, whether that happens or not, it is clear to me that Haskell is a point in the design space unlike any other, and we learn almost as much from the things it gets wrong as we do from the things it gets right.

                                    It’s been a wonderful two years, Haskell. I won’t be a stranger.

                                      \ No newline at end of file diff --git a/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/index.html b/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/index.html new file mode 100644 index 0000000..c6796c8 --- /dev/null +++ b/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/index.html @@ -0,0 +1,274 @@ +Reimplementing Hackett’s type language: expanding to custom core forms in Racket

                                      Reimplementing Hackett’s type language: expanding to custom core forms in Racket

                                      ⦿ racket, hackett, macros

                                      In the past couple of weeks, I completely rewrote the implementation of Hackett’s type language to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.

                                      This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like my previous blog post on Hackett, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.

                                      What are core forms?

                                      Before we can get started writing custom core forms, we need to understand the meaning of Racket’s plain old core forms. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.

                                      To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a #lang line at the top. In this case, we’ll use #lang racket to keep things simple:

                                      #lang racket
                                      +
                                      +(define (add2 x)
                                      +  (+ x 2))
                                      +
                                      +(add2 3)

                                      How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as reading the program. The #lang line controls how the program is read—some #langs provide parsers that allow syntax that is very different from the parser used for #lang racket—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:

                                      (module m racket
                                      +  (#%module-begin
                                      +    (define (add2 x)
                                      +      (+ x 2))
                                      +
                                      +    (add2 3)))

                                      Note the introduction of #%module-begin. Despite the fancy name, this is really just an ordinary macro provided by the racket language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with #%module-begin; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.

                                      One the program has been read, it is subsequently expanded by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:

                                      (module m racket
                                      +  (#%plain-module-begin
                                      +    (define-values (add2)
                                      +      (lambda (x) (#%plain-app + x '2)))
                                      +
                                      +    (#%plain-app call-with-values
                                      +                 (lambda () (#%plain-app add2 '3))
                                      +                 print-values)))

                                      Let’s note the things that changed:

                                      1. #%module-begin was replaced with #%plain-module-begin. #%plain-module-begin is a binding that wraps the body of every expanded module, and all definitions of #%module-begin in any language must eventually expand to #%plain-module-begin. However, #lang racket’s #%module-begin doesn’t just expand to #%plain-module-begin, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints 5 even though there is no code related to printing in the original program!

                                      2. The lambda shorthand used with define was converted to an explicit use of lambda, and it was expanded to define-values. In Racket, define and define-syntax are really just macros for define-values and define-syntaxes that only bind a single identifier.

                                      3. All function applications were tagged explicitly with #%plain-app. This syntactically distinguishes function applications from uses of forms like define-values or lambda. It also allows languages to customize function application by providing their own macros named #%app (just like languages can provide their own macros named #%module-begin that expand to #%plain-module-begin), but that is outside the scope of this blog post.

                                      4. All literals have been wrapped with quote, so 2 became '2 and 3 became '3.

                                      Importantly, the resulting program contains no macros. Such programs are called fully expanded, since all macros have been eliminated and no further expansion can take place.

                                      So what’s left behind? Well, some of the things in the program are literal data, like the numbers 2 and 3. There are also some variable references, x and add2. Most of the program, however, is built out of primitives like module, #%plain-module-begin, #%plain-app, define-values, and lambda. These primitives are core forms—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.

                                      In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:

                                      var add2 =
                                      +  function (x) { return x + 2; };
                                      +
                                      +console.log(add2(3));

                                      Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be compiled. The Racket compiler has built-in rules for how to compile core forms like define-values, lambda, and #%plain-app, and the result is optimized Racket bytecode.

                                      In the remainder of this blog post, as most discussions of macros do, we’ll ignore the read and compile steps of the Racket program pipeline and focus exclusively on the expand step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.

                                      Racket’s default core forms

                                      So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered module, #%plain-module-begin, #%plain-app, define-values, lambda, and quote, but there are many more. The full list is available in the section of the Racket reference named Fully Expanded Programs, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like define-syntaxes, if, let-values, letrec-values, begin, quote-syntax, and set!. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.

                                      These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, #lang racket implements cond as a macro that eventually expands into if, and it implements syntax as a macro that eventually expands into function calls and quote-syntax. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s match can expand into uses of let and cond, and it doesn’t need to concern itself with using let-values and if. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.

                                      The need for custom core forms

                                      With that in mind, why might we wish to define custom core forms? In fact, what would such a thing even mean? By their very nature, all Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.

                                      Despite this, there are at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.

                                      Supporting multiple backends

                                      The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket #lang that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might also wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.

                                      In the case of a hardware description language, the custom core forms might include things like input and output for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could additionally define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.

                                      Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.

                                      Compiling an extensible embedded language

                                      A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.

                                      This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it will eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.

                                      Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the match macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.

                                      Existing DSLs of this type are rare, but they do exist. syntax/parse provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from racket/match to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While match is essentially just a traditional pattern-matcher, albeit an extensible one, syntax-parse is its own programming language, closer in some ways to Prolog than to Racket.

                                      For this reason, syntax/parse has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as pattern directives. Here is an example of pattern directives in action, from my own threading library:

                                      [(_ ex:expr cl:clause remaining:clause ...)
                                      + #:do [(define call (syntax->list #'cl.call))
                                      +       (define-values (pre post)
                                      +         (split-at call (add1 (or (attribute cl.insertion-point) 0))))]
                                      + #:with [pre ...] pre
                                      + #:with [post ...] post
                                      + #:with app/ctx (adjust-outer-context this-syntax #'(pre ... ex post ...) #'cl)
                                      + (adjust-outer-context this-syntax #'(~> app/ctx remaining ...) this-syntax)]

                                      Each directive is represented by a keyword, in this case #:do and #:with. Each directive has a corresponding keyword in the pattern language, in this case ~do and ~parse. Therefore, the above pattern could equivalently be written this way:

                                      [{~and (_ ex:expr cl:clause remaining:clause ...)
                                      +       {~do (define call (syntax->list #'cl.call))
                                      +            (define-values (pre post)
                                      +              (split-at call (add1 (or (attribute cl.insertion-point) 0))))}
                                      +       {~parse [pre ...] pre}
                                      +       {~parse [post ...] post}
                                      +       {~parse app/ctx (adjust-outer-context this-syntax #'(pre ... ex post ...) #'cl)}}
                                      + (adjust-outer-context this-syntax #'(~> app/ctx remaining ...) this-syntax)]

                                      The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using #:declare, so this is also equivalent:

                                      [(_ ex cl remaining ...)
                                      + #:declare ex expr
                                      + #:declare cl clause
                                      + #:declare remaining clause
                                      + #:do [(define call (syntax->list #'cl.call))
                                      +       (define-values (pre post)
                                      +         (split-at call (add1 (or (attribute cl.insertion-point) 0))))]
                                      + #:with [pre ...] pre
                                      + #:with [post ...] post
                                      + #:with app/ctx (adjust-outer-context this-syntax #'(pre ... ex post ...) #'cl)
                                      + (adjust-outer-context this-syntax #'(~> app/ctx remaining ...) this-syntax)]

                                      This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a #:with or ~parse pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using #:do or ~do, practical uses of syntax/parse really do involve quite a lot of programming in its pattern DSL.

                                      But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—#:declare, #:do, and #:with, among others—are essentially the core forms of syntax/parse’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.

                                      But what if syntax/parse could define its own core forms? What if, instead of #:do, #:declare, and #:with being implemented as keyword options specially recognized by the syntax-parse grammar, it defined do, declare, and with as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the syntax/parse core forms. The implementation of syntax/parse could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.

                                      Now, to be fair, syntax/parse is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.

                                      The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.

                                      The need for a custom type language in Hackett

                                      In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for both of them:

                                      • Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.

                                      • Hackett can also make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.

                                      The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:

                                      • (#%type:con id) — Type constructors, like Integer or Maybe. These are one of the fundamental building blocks of Hackett types.

                                      • (#%type:app type type) — Type application, such as (Maybe Integer). Types are curried, so type constructors that accept multiple arguments are represented by nested uses of #%type:app.

                                      • (#%type:forall id type) — Universal quantification. This is essentially a binding form, which binds any uses of (#%type:bound-var id) in type.

                                      • (#%type:qual type type) — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like Eq are bound as type constructors.

                                      • Finally, Hackett types support three different varieties of type variables:

                                        • (#%type:bound-var id) — Bound type variables. These are only legal under a corresponding #%type:forall.

                                        • (#%type:wobbly-var id) — Solver variables, which may unify with any other type as part of the typechecking process.

                                        • (#%type:rigid-var id) — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.

                                      To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we can bind them to macros that do nothing but raise an exception if something attempts to expand them:

                                      (define-syntaxes [#%type:con #%type:app #%type:forall #%type:qual
                                      +                  #%type:bound-var #%type:wobbly-var #%type:rigid-var]
                                      +  (let ([type-literal (λ (stx) (raise-syntax-error #f "cannot be used as an expression" stx))])
                                      +    (values type-literal type-literal type-literal type-literal
                                      +            type-literal type-literal type-literal)))

                                      This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.

                                      Expanding types in our type language

                                      We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we do want to expand some of their subforms. For example, in the type (#%type:app a b), we want to recursively expand a and b.

                                      In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called local-expand, and it has an option relevant to our needs: the stop list.

                                      Often, local-expand is used to force the expander to completely, recursively expand a form. For example, by using local-expand, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:

                                      (local-expand #'(let ([x 1]) (+ x 2)) 'expression '())
                                      +; => (let-values ([(x) '1]) (#%plain-app + x '2))

                                      The third argument to local-expand is the stop list, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:

                                      (begin-for-syntax
                                      +  (define type-literal-ids
                                      +    (list #'#%type:con #'#%type:app #'#%type:forall #'#%type:qual
                                      +          #'#%type:bound-var #'#%type:wobbly-var #'#%type:rigid-var))
                                      +
                                      +  (local-expand #'(#%type:forall x t) 'expression type-literal-ids))
                                      +  ; => (#%type:forall x t)

                                      Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the #%type:forall identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding t since the expander has no way of knowing which pieces of (#%type:forall x t) it should expand! In this case, we want it to recur to expand t, since it should be a type, but not x, since #%type:forall essentially puts x in binding position.

                                      Therefore, we have to get more clever. We need to call local-expand to produce a type, then we have to pattern-match on it and subsequently call local-expand again on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.

                                      One good way to do this is to use syntax/parse syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using local-expand and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled #%type:con and #%type:app:

                                      (begin-for-syntax
                                      +  (define-literal-set type-literals
                                      +    [#%type:con #%type:app #%type:forall #%type:qual
                                      +     #%type:bound-var #%type:wobbly-var #%type:rigid-var])
                                      +
                                      +  (define-syntax-class type
                                      +    #:description "type"
                                      +    #:attributes [expansion]
                                      +    [pattern _ #:with :expanded-type
                                      +                      (local-expand this-syntax 'expression type-literal-ids)])
                                      +
                                      +  (define-syntax-class expanded-type
                                      +    #:description #f
                                      +    #:attributes [expansion]
                                      +    #:commit
                                      +    #:literal-sets [type-literals]
                                      +    [pattern (#%type:con ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:app ~! a:type b:type)
                                      +             #:attr expansion #'(#%type:app a.expansion b.expansion)]))

                                      This blog post is definitely not a syntax/parse tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named expansion. This attribute contains the fully expanded version of the type currently being parsed. In the #%type:con case, expansion is just this-syntax, which holds the current piece of syntax being parsed. This makes sense, since uses of #%type:con just expand to themselves—expanding (#%type:con Maybe) should not perform any additional expansion on Maybe. This is one of Hackett’s atomic types.

                                      In contrast, #%type:app does recursively expand its arguments. By annotating its two subforms with :type, the type syntax class will invoke local-expand on each subform, which will in turn use expanded-type to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once a and b have been expanded, #%type:app reassembles them into a new syntax object using #'(#%type:app a.expansion b.expansion), which replaces their unexpanded versions with their new, expanded versions.

                                      We can see this behavior by writing a small expand-type function that will expand its argument:

                                      (begin-for-syntax
                                      +  (define expand-type (syntax-parser [t:type #'t.expansion])))

                                      Now we can use it to observe what happens when we try expanding a type using #%type:app:

                                      (expand-type #'(#%type:app Maybe Integer))
                                      +; => #%type:app: expected type
                                      +;      at: Maybe
                                      +;      in: (#%type:app Maybe Integer)

                                      Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined Maybe or Integer anywhere. Let’s do so! We can define them as simple macros that expand into uses of #%type:con, which can be done easily using make-variable-like-transformer from syntax/transformer:

                                      (define-syntax Maybe (make-variable-like-transformer #'(#%type:con Maybe)))
                                      +(define-syntax Integer (make-variable-like-transformer #'(#%type:con Integer)))

                                      Now, if we try expanding that same type again:

                                      (expand-type #'(#%type:app Maybe Integer))
                                      +; => (#%type:app (#%type:con Maybe) (#%type:con Integer))

                                      …it works! Neat. Now we just need to add the cases for the remaining forms in our type language:

                                      (begin-for-syntax
                                      +  (define-syntax-class expanded-type
                                      +    #:description #f
                                      +    #:attributes [expansion]
                                      +    #:commit
                                      +    #:literal-sets [type-literals]
                                      +    [pattern (#%type:con ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:app ~! a:type b:type)
                                      +             #:attr expansion #'(#%type:app a.expansion b.expansion)]
                                      +    [pattern (#%type:forall ~! x:id t:type)
                                      +             #:attr expansion #'(#%type:forall x t.expansion)]
                                      +    [pattern (#%type:qual ~! a:type b:type)
                                      +             #:attr expansion #'(#%type:qual a.expansion b.expansion)]
                                      +    [pattern (#%type:bound-var ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:wobbly-var ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:rigid-var ~! _:id)
                                      +             #:attr expansion this-syntax]))

                                      This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like let-syntax to work in types. For example, we ought to be able to create a local type binding with let-syntax and have it just work. Unfortunately, it doesn’t:

                                      (expand-type #'(let-syntax ([Bool (make-variable-like-transformer #'(#%type:con Bool))])
                                      +                 (#%type:app Maybe Bool)))
                                      +; => let-syntax: expected one of these identifiers: `#%type:con', `#%type:app', `#%type:forall', `#%type:qual', `#%type:bound-var', `#%type:wobbly-var', or `#%type:rigid-var'
                                      +;     at: letrec-syntaxes+values
                                      +;     in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))

                                      What went wrong? And why is it complaining about letrec-syntaxes+values? Well, if you read the documentation for local-expand, you’ll find that its behavior is a little more complicated than you might at first believe:

                                      If stop-ids is [a nonempty list containing more than just module*], then begin, quote, set!, #%plain-lambda, case-lambda, let-values, letrec-values, if, begin0, with-continuation-mark, letrec-syntaxes+values, #%plain-app, #%expression, #%top, and #%variable-reference are implicitly added to stop-ids. Expansion stops when the expander encounters any of the forms in stop-ids, and the result is the partially-expanded form.

                                      That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with letrec-syntaxes+values (which let-syntax expands to) is a reasonable one. If the expander naïvely expanded letrec-syntaxes+values in the presence of a nonempty stop list, it could cause some significant problems!

                                      Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:

                                      (let-syntax ([Bool (make-variable-like-transformer #'(#%type:con Bool))])
                                      +  (#%type:app Maybe Bool))

                                      We see let-syntax, so we start by evaluating the expression on the right hand side of the Bool binding. This produces a transformer expression, so we bind Bool to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:

                                      ; local bindings:
                                      +;   Bool -> #<variable-like-transformer>
                                      +(#%type:app Maybe Bool)

                                      Now, the identifier in application position is #%type:app, and #%type:app is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the let-syntax needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is (#%type:app Maybe Bool). But this is a problem, because when we then go to expand Bool, Bool isn’t in the local binding table anymore! The let-syntax was already erased, and Bool is unbound!

                                      When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.

                                      Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call local-expand. The bad news is that first-class definition contexts are complicated, and using them properly is a surprisingly subtle problem.

                                      Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our type and expanded-type syntax classes so that we may thread a definition context around as we recursively expand:

                                      (begin-for-syntax
                                      +  (define-syntax-class (type [intdef-ctx #f])
                                      +    #:description "type"
                                      +    #:attributes [expansion]
                                      +    [pattern _ #:with {~var || (expanded-type intdef-ctx)}
                                      +                      (local-expand this-syntax 'expression type-literal-ids intdef-ctx)])
                                      +
                                      +  (define-syntax-class (expanded-type intdef-ctx)
                                      +    #:description #f
                                      +    #:attributes [expansion]
                                      +    #:commit
                                      +    #:literal-sets [type-literals]
                                      +    [pattern (#%type:con ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)})
                                      +             #:attr expansion #'(#%type:app a.expansion b.expansion)]
                                      +    [pattern (#%type:forall ~! x:id {~var t (type intdef-ctx)})
                                      +             #:attr expansion #'(#%type:forall x t.expansion)]
                                      +    [pattern (#%type:qual ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)})
                                      +             #:attr expansion #'(#%type:qual a.expansion b.expansion)]
                                      +    [pattern (#%type:bound-var ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:wobbly-var ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:rigid-var ~! _:id)
                                      +             #:attr expansion this-syntax]))

                                      Now, we can add an additional case to expanded-type to handle letrec-syntaxes+values, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:

                                      [pattern (letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr)
                                      +         #:do [(define intdef-ctx* (syntax-local-make-definition-context))
                                      +               (for ([ids (in-list (attribute id))]
                                      +                     [e (in-list (attribute e))])
                                      +                 (syntax-local-bind-syntaxes ids e intdef-ctx*))]
                                      +         #:with {~var t* (type intdef-ctx*)} #'t
                                      +         #:attr expansion #'t*.expansion]

                                      But even this isn’t quite right. The problem with this implementation is that it throws away the existing intdef-ctx argument to expanded-type, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a child of the previous definition context by passing the old context as an argument to syntax-local-make-definition-context. This will ensure the parent bindings are brought into scope when expanding using the child context:

                                      [pattern (letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr)
                                      +         #:do [(define intdef-ctx* (syntax-local-make-definition-context intdef-ctx))
                                      +               (for ([ids (in-list (attribute id))]
                                      +                     [e (in-list (attribute e))])
                                      +                 (syntax-local-bind-syntaxes ids e intdef-ctx*))]
                                      +         #:with {~var t* (type intdef-ctx*)} #'t
                                      +         #:attr expansion #'t*.expansion]

                                      With this in place, our example using let-syntax actually works!

                                      (expand-type #'(let-syntax ([Bool (make-variable-like-transformer #'(#%type:con Bool))])
                                      +                 (#%type:app Maybe Bool)))
                                      +; => (#%type:app (#%type:con Maybe) (#%type:con Bool))

                                      Pretty cool, isn’t it?

                                      Preserving syntax properties and source locations

                                      We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.

                                      To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports #%type:app under a different name, like #%type:apply, we should expand to a piece of syntax that still has #%type:apply in application position instead of replacing it with #%type:app.

                                      To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the #%type:app clause to the following:

                                      [pattern (head:#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)})
                                      +         #:attr expansion #'(head a.expansion b.expansion)]

                                      But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy everything, we can define two helper macros, syntax/loc/props and quasisyntax/loc/props, which are like syntax/loc and quasisyntax/loc but copy properties in addition to source location information:

                                      (begin-for-syntax
                                      +  (define-syntaxes [syntax/loc/props quasisyntax/loc/props]
                                      +    (let ()
                                      +      (define (make-syntax/loc/props name syntax-id)
                                      +        (syntax-parser
                                      +          [(_ from-stx-expr:expr {~describe "template" template})
                                      +           #`(let ([from-stx from-stx-expr])
                                      +               (unless (syntax? from-stx)
                                      +                 (raise-argument-error '#,name "syntax?" from-stx))
                                      +               (let* ([stx (#,syntax-id template)]
                                      +                      [stx* (syntax-disarm stx #f)])
                                      +                 (syntax-rearm (datum->syntax stx* (syntax-e stx*) from-stx from-stx) stx)))]))
                                      +      (values (make-syntax/loc/props 'syntax/loc/props #'syntax)
                                      +              (make-syntax/loc/props 'quasisyntax/loc/props #'quasisyntax)))))

                                      Using syntax/loc/props, we can be truly thorough about ensuring all properties are preserved:

                                      [pattern (head:#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)})
                                      +         #:attr expansion (syntax/loc/props this-syntax
                                      +                            (head a.expansion b.expansion))]

                                      Applying this to the other relevant clauses, we get an updated version of the expanded-type syntax class:

                                      (begin-for-syntax
                                      +  (define-syntax-class (expanded-type intdef-ctx)
                                      +    #:description #f
                                      +    #:attributes [expansion]
                                      +    #:commit
                                      +    #:literal-sets [kernel-literals type-literals]
                                      +    [pattern (letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr)
                                      +             #:do [(define intdef-ctx* (syntax-local-make-definition-context intdef-ctx))
                                      +                   (for ([ids (in-list (attribute id))]
                                      +                         [e (in-list (attribute e))])
                                      +                     (syntax-local-bind-syntaxes ids e intdef-ctx*))]
                                      +             #:with {~var t* (type intdef-ctx*)} #'t
                                      +             #:attr expansion #'t*.expansion]
                                      +    [pattern (#%type:con ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (head:#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)})
                                      +             #:attr expansion (syntax/loc/props this-syntax
                                      +                                (head a.expansion b.expansion))]
                                      +    [pattern (head:#%type:forall ~! x:id {~var t (type intdef-ctx)})
                                      +             #:attr expansion (syntax/loc/props this-syntax
                                      +                                (head x t.expansion))]
                                      +    [pattern (head:#%type:qual ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)})
                                      +             #:attr expansion (syntax/loc/props this-syntax
                                      +                                (head a.expansion b.expansion))]
                                      +    [pattern (#%type:bound-var ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:wobbly-var ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:rigid-var ~! _:id)
                                      +             #:attr expansion this-syntax]))

                                      Now we’re getting closer, but if you can believe it, even this isn’t good enough. The real expander’s implementation of letrec-syntaxes+values does two things our implementation does not: it copies properties and updates the 'origin property to indicate the syntax came from a use of letrec-syntaxes+values, and it adds a 'disappeared-use property to record the erased bindings for use by tools like DrRacket. We can apply syntax-track-origin and internal-definition-context-track to the resulting syntax to add the same properties the expander would:

                                      [pattern (head:letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr)
                                      +         #:do [(define intdef-ctx* (syntax-local-make-definition-context intdef-ctx))
                                      +               (for ([ids (in-list (attribute id))]
                                      +                     [e (in-list (attribute e))])
                                      +                 (syntax-local-bind-syntaxes ids e intdef-ctx*))]
                                      +         #:with {~var t* (type intdef-ctx*)} #'t
                                      +         #:attr expansion (~> (internal-definition-context-track intdef-ctx* #'t*.expansion)
                                      +                              (syntax-track-origin this-syntax #'head))]

                                      Now we’ve finally dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.

                                      Connecting our custom language to Hackett

                                      It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:

                                      #lang racket/base
                                      +
                                      +(require (for-meta 2 racket/base
                                      +                     syntax/parse)
                                      +         (for-syntax racket/base
                                      +                     syntax/intdef
                                      +                     threading)
                                      +         syntax/parse/define)
                                      +
                                      +(begin-for-syntax
                                      +  (define-syntaxes [syntax/loc/props quasisyntax/loc/props]
                                      +    (let ()
                                      +      (define (make-syntax/loc/props name syntax-id)
                                      +        (syntax-parser
                                      +          [(_ from-stx-expr:expr {~describe "template" template})
                                      +           #`(let ([from-stx from-stx-expr])
                                      +               (unless (syntax? from-stx)
                                      +                 (raise-argument-error '#,name "syntax?" from-stx))
                                      +               (let* ([stx (#,syntax-id template)]
                                      +                      [stx* (syntax-disarm stx #f)])
                                      +                 (syntax-rearm (datum->syntax stx* (syntax-e stx*) from-stx from-stx) stx)))]))
                                      +      (values (make-syntax/loc/props 'syntax/loc/props #'syntax)
                                      +              (make-syntax/loc/props 'quasisyntax/loc/props #'quasisyntax)))))
                                      +
                                      +(define-syntaxes [#%type:con #%type:app #%type:forall #%type:qual
                                      +                  #%type:bound-var #%type:wobbly-var #%type:rigid-var]
                                      +  (let ([type-literal (λ (stx) (raise-syntax-error #f "cannot be used as an expression" stx))])
                                      +    (values type-literal type-literal type-literal type-literal
                                      +            type-literal type-literal type-literal)))
                                      +
                                      +(begin-for-syntax
                                      +  (define type-literal-ids
                                      +    (list #'#%type:con #'#%type:app #'#%type:forall #'#%type:qual
                                      +          #'#%type:bound-var #'#%type:wobbly-var #'#%type:rigid-var))
                                      +
                                      +  (define-literal-set type-literals
                                      +    [#%type:con #%type:app #%type:forall #%type:qual
                                      +     #%type:bound-var #%type:wobbly-var #%type:rigid-var])
                                      +
                                      +  (define-syntax-class (type [intdef-ctx #f])
                                      +    #:description "type"
                                      +    #:attributes [expansion]
                                      +    [pattern _ #:with {~var || (expanded-type intdef-ctx)}
                                      +                      (local-expand this-syntax 'expression type-literal-ids intdef-ctx)])
                                      +
                                      +  (define-syntax-class (expanded-type intdef-ctx)
                                      +    #:description #f
                                      +    #:attributes [expansion]
                                      +    #:commit
                                      +    #:literal-sets [kernel-literals type-literals]
                                      +    [pattern (head:letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr)
                                      +             #:do [(define intdef-ctx* (syntax-local-make-definition-context intdef-ctx))
                                      +                   (for ([ids (in-list (attribute id))]
                                      +                         [e (in-list (attribute e))])
                                      +                     (syntax-local-bind-syntaxes ids e intdef-ctx*))]
                                      +             #:with {~var t* (type intdef-ctx*)} #'t
                                      +             #:attr expansion (~> (internal-definition-context-track intdef-ctx* #'t*.expansion)
                                      +                                  (syntax-track-origin this-syntax #'head))]
                                      +    [pattern (#%type:con ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (head:#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)})
                                      +             #:attr expansion (syntax/loc/props this-syntax
                                      +                                (head a.expansion b.expansion))]
                                      +    [pattern (head:#%type:forall ~! x:id {~var t (type intdef-ctx)})
                                      +             #:attr expansion (syntax/loc/props this-syntax
                                      +                                (head x t.expansion))]
                                      +    [pattern (head:#%type:qual ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)})
                                      +             #:attr expansion (syntax/loc/props this-syntax
                                      +                                (head a.expansion b.expansion))]
                                      +    [pattern (#%type:bound-var ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:wobbly-var ~! _:id)
                                      +             #:attr expansion this-syntax]
                                      +    [pattern (#%type:rigid-var ~! _:id)
                                      +             #:attr expansion this-syntax])
                                      +
                                      +  (define expand-type (syntax-parser [t:type #'t.expansion])))

                                      But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a typechecker. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.

                                      But we don’t just need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom #%app binding that converts n-ary type applications to nested uses of #%type:app, as well as a nicer forall macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of #%app—one for types and one for values—since Hackett supports multiple namespaces, and each can have its own #%app binding.

                                      The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom syntax/parse pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, it’s available on GitHub here.

                                      Evaluation, limitations, and acknowledgements

                                      Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started my new job this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net removal of 100 lines from the rest of the codebase, which I consider an organizational win.

                                      As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I able to get it working quickly and easily, I was able to do it in no more than 20 lines of code. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.

                                      Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the local-expand stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes syntax-parameterize, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like syntax-parameterize cooperate better with things like Hackett’s type language.

                                      Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to Matthew Flatt, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank Ryan Culpepper for his incredible work on constructing tools for the working macro developer, including writing the fantastic syntax/parse library that powers essentially everything I do. Thank you both.

                                        \ No newline at end of file diff --git a/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/index.html b/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/index.html new file mode 100644 index 0000000..7f9b346 --- /dev/null +++ b/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/index.html @@ -0,0 +1,352 @@ +Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions

                                        Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions

                                        ⦿ racket, macros

                                        In my previous blog post, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.

                                        What are internal definitions?

                                        This blog post is going to be largely focused on how to properly implement a form that handles the expansion of internal definitions in Racket. This is a tricky topic to get right, but before we can discuss internal definitions, we have to establish what definitions themselves are and how they relate to other binding forms.

                                        In a traditional Lisp, there are two kinds of bindings: top-level bindings and local bindings. In Scheme and its descendants, this distinction is characterized by two different binding forms, define and let. To a first approximation, define is used for defining top-level, global bindings, and it resembles variable definitions in many mainstream languages in the sense that definitions using define are not really expressions. They don’t produce a value, they define a new binding. Definitions written with define look like this:

                                        (define x 42)
                                        +(define y "hello")

                                        Each definition is made up of two parts: the binding identifier, in this case x and y, and the right hand side, or RHS for short. Each RHS is a single expression that will be evaluated and used as the value for the introduced binding.

                                        In Scheme and Racket, define also supports a shorthand form for defining functions in a natural syntax without the explicit need to write lambda, which looks like this:

                                        (define (double x)
                                        +  (* x 2))

                                        However, this is just syntactic sugar. The above form is really just a macro for the following equivalent, expanded version:

                                        (define double
                                        +  (lambda (x) (* x 2)))

                                        Since we only care about fully-expanded programs, we’ll focus exclusively on the expanded version of define in this blog post, since if we handle that, we’ll also handle the function shorthand’s expansion.

                                        In contrast to define, there is also let, which has a rather different shape. A let form is an expression, and it creates local bindings in a delimited scope:

                                        (let ([x 2]
                                        +      [y 3])
                                        +  (+ x y))

                                        The binding clauses of a let expression are known as the binding pairs, and the sequence of expressions afterwards are known as the body of the let. Each binding pair consists of a binding identifier and a RHS, just like a top-level definition created with define, but while define is a standalone form, the binding pairs cannot meaningfully exist outside of a let—they are recognized as part of the grammar of the let form itself.

                                        Like other Lisps, Racket distinguishes between top-level—or, more precisely, module-level—bindings and local bindings. A module-level binding can be exported using provide, which will allow other modules to access the binding by importing the module with require. Such definitions are treated specially by the macroexpander, compiler, and runtime system alike. There is a pervasive, meaningful difference between module-level definitions and local definitions besides simply scope.

                                        I am making an effort to make this as clear as possible before discussing internal definitions because without it, the following point can be rather confusing: internal definitions are written using define, but they are local bindings, not module-level ones! In Racket, define is allowed to appear in the body of virtually all block forms like let, so the following is a legal program:

                                        (let ()
                                        +  (define x 2)
                                        +  (define y 3)
                                        +  (+ x y))

                                        This program is equivalent to the one expressed using let. In fact, when the Racket macroexpander expands these local uses of define, it actually translates them into uses of letrec. After expanding the above expression, it would look closer to the following:

                                        (let ()
                                        +  (letrec ([x 2]
                                        +           [y 3])
                                        +    (+ x y)))

                                        In this sense, define is a form with a double life in Racket. When used at the module level, it creates module-level definitions, which remain in a fully-expanded program and can be imported by other modules. When used inside local blocks, it creates internal definitions, which do not remain in fully expanded programs, since they are translated into recursive local binding forms.

                                        In this blog post, we will ignore module-level definitions. Like in the previous blog post, we will focus exclusively on expanding expressions, not whole modules. However, we will extend our language to allow internal definitions inside local binding forms, and we will translate them into letrec forms in the same way as the Racket macroexpander.

                                        Revisiting and generalizing the expression expander

                                        In the previous blog post, our expander expanded types, which were essentially expressions from the perspective of the Racket macroexpander. We wrote a syntax class that handled the parsing of a restricted type grammar that disallowed most Racket-level expression forms, like begin, if, #%plain-lambda, and quote. After all, Hackett is not dependently-typed, and it disallows explicit type abstraction to preserve type inference, so it would be a very bad thing if we allowed if or explicit lambda abstraction to appear in our types. For this blog post, however, we will restructure the type expander to handle the full grammar of expressions permitted by Racket.

                                        While the syntax class approach used in the previous blog post was cute, this blog post will use ordinary functions defined at phase 1 instead of syntax classes. In practice, this provides superior error reporting, since it reports syntax errors in terms of the form that went wrong, not the form prior to expansion. Since we can still use syntax-parse to parse the arguments to these functions, we don’t lose any expressive power in the expression of our pattern language.

                                        To start, we’ll extract the call to local-expand into its own function. This corresponds to the type syntax class from the previous blog post, but we’ll use phase 1 parameters to avoid threading so many explicit function arguments around:

                                        (begin-for-syntax
                                        +  (define current-context (make-parameter #f))
                                        +  (define current-stop-list (make-parameter (list #'define-values #'define-syntaxes)))
                                        +  (define current-intdef-ctx (make-parameter #f))
                                        +
                                        +  (define (current-expand stx)
                                        +    (local-expand stx
                                        +                  (current-context)
                                        +                  (current-stop-list)
                                        +                  (current-intdef-ctx))))

                                        Due to the way local-expand implicitly extends the stop list, as discussed in the previous blog post, we can initialize the stop list to a list containing just define-values and define-syntaxes, and the other forms we care about will be included automatically.

                                        Next, we’ll use this function to implement a expand-expression function, which will emulate the way the expander expands a single expression, as the name implies. We’ll ignore any custom core forms for now, so we’ll just focus exclusively on the Racket core forms.

                                        A few of Racket’s core forms are not actually subject to any expansion at all, and they expand to themselves. These forms are quote, quote-syntax, and #%variable-reference. Additionally, #%top is not something useful to handle ourselves, since it involves no recursive expansion, so we’ll treat it as if it expands to itself as well and allow the expander to raise any unbound identifier errors it produces. Here’s what the expand-expression function looks like when exclusively handling these things:

                                        (define (expand-expression stx)
                                        +    (syntax-parse (parameterize ([current-context 'expression])
                                        +                    (current-expand stx))
                                        +      #:literal-sets [kernel-literals]
                                        +      [({~or quote quote-syntax #%top #%variable-reference} ~! . _)
                                        +       this-syntax]))

                                        Another set of Racket core forms are simple expressions which contain subforms, all of which are themselves expressions. These forms include things like #%expression, begin, and if, and they can be expanded recursively. We’ll add another clause to handle these, which can be written with a straightforward recursive call to expand-expression:

                                        [({~and head {~or #%expression #%plain-app begin begin0 if with-continuation-mark}} ~! form ...)
                                        + #:with [form* ...] (map expand-expression (attribute form))
                                        + (syntax/loc/props this-syntax
                                        +   (head form* ...))]

                                        Another easy form to handle is set!, since it also requires simple recursive expansion, but it can’t be handled in the same way as the above forms since one of its subforms (the variable to mutate) should not be expanded. It needs another small clause:

                                        [(head:set! ~! x:id rhs)
                                        + (quasisyntax/loc/props this-syntax
                                        +   (head x #,(expand-expression #'rhs)))]

                                        The other expressions are harder, since they’re all the binding forms. Fully-expanded Racket code has four local binding forms: #%plain-lambda, case-lambda, let-values, and letrec-values. Additionally, as discussed in the previous blog post, local-expand can also produce letrec-syntaxes+values forms produced by local syntax bindings. In the type expander, we completely disallowed runtime bindings from appearing in the resulting program, so we completely removed letrec-syntaxes+values in our expansion, but in the case of handling arbitrary Racket programs, we actually want to leave a letrec-values form behind to hold any runtime bindings (i.e. the values part of letrec-syntaxes+values).

                                        We’ll start with #%plain-lambda, which is the simplest of all the five aforementioned binding forms. It binds a sequence of identifiers at runtime, and they are in scope within the body of the lambda expression. Just as we created and used an internal-definition context to hold the bindings of a letrec-syntax+values form in the previous blog post, we’ll do the same for Racket’s other binding forms as well:

                                        [(head:#%plain-lambda ~! [x:id ...] body ...)
                                        + #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +       (syntax-local-bind-syntaxes (attribute x) #f intdef-ctx)]
                                        + #:with [x* ...] (internal-definition-context-introduce intdef-ctx #'[x ...])
                                        + #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                      (map expand-expression (attribute body)))
                                        + (syntax/loc/props this-syntax
                                        +   (head [x* ...] body* ...))]

                                        However, the above handling of #%plain-lambda isn’t quite right, since the argument list can also include a “rest argument” binding in addition to a sequence of positional arguments. To accommodate this, we can introduce a simple syntax class that handles the different permutations:

                                        (begin-for-syntax
                                        +  (define-syntax-class plain-formals
                                        +    #:description "formals"
                                        +    #:attributes [[id 1]]
                                        +    #:commit
                                        +    [pattern (id:id ...)]
                                        +    [pattern (id*:id ... . id**:id) #:with [id ...] #'[id* ... id**]]))

                                        Now we can use this to adjust #%plain-lambda to handle rest arguments:

                                        [(head:#%plain-lambda ~! formals:plain-formals body ...)
                                        + #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +       (syntax-local-bind-syntaxes (attribute formals.id) #f intdef-ctx)]
                                        + #:with formals* (internal-definition-context-introduce intdef-ctx #'formals)
                                        + #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                      (map expand-expression (attribute body)))
                                        + (syntax/loc/props this-syntax
                                        +   (head formals* body* ...))]

                                        Next, we’ll handle case-lambda. As it turns out, expanding case-lambda is almost exactly the same as expanding #%plain-lambda, except that it has multiple clauses. Since each clause is expanded identically to the body of a #%plain-lambda, and it even has the same shape, the clauses can be extracted into a separate syntax class to share code between the two forms:

                                        (begin-for-syntax
                                        +  (define-syntax-class lambda-clause
                                        +    #:description #f
                                        +    #:attributes [expansion]
                                        +    #:commit
                                        +    [pattern [formals:plain-formals body ...]
                                        +             #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +                   (syntax-local-bind-syntaxes (attribute formals.id) #f intdef-ctx)]
                                        +             #:with formals* (internal-definition-context-introduce intdef-ctx #'formals)
                                        +             #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                                  (map expand-expression (attribute body)))
                                        +             #:attr expansion #'[formals* body* ...]]))

                                        Now, both #%plain-lambda and case-lambda can be handled in a few lines of code each:

                                        [(head:#%plain-lambda ~! . clause:lambda-clause)
                                        + (syntax/loc/props this-syntax
                                        +   (head . clause.expansion))]
                                        +
                                        +[(head:case-lambda ~! clause:lambda-clause ...)
                                        + (syntax/loc/props this-syntax
                                        +   (head clause.expansion ...))]

                                        Finally, we need to tackle the three let forms. None of these involve any fundamentally new ideas, but they are a little bit more involved than the variants of lambda due to the need to handle the RHSs. Each variant is slightly different, but not dramatically so: the bindings aren’t in scope when expanding the RHSs of let-values, but they are for letrec-values and letrec-syntaxes+values, and letrec-syntaxes+values creates transformer bindings and must evaluate some RHSs in phase 1 while let-values and letrec-values exclusively bind runtime bindings. It would be possible to implement these three forms in separate clauses, but since we’d ideally like to duplicate as little code as possible, we can write a rather elaborate syntax/parse pattern to handle all three binding forms all at once.

                                        We’ll start by handling let-values alone to keep things simple:

                                        [(head:let-values ~! ([(x:id ...) rhs] ...) body ...)
                                        + #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +       (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx)]
                                        + #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...])
                                        + #:with [rhs* ...] (map expand-expression (attribute rhs))
                                        + #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                      (map expand-expression (attribute body)))
                                        + (syntax/loc/props this-syntax
                                        +   (head ([(x* ...) rhs*] ...) body* ...))]

                                        This isn’t dramatically different from the implementation of #%plain-lambda. The only difference is that we have to recursively invoke expand-expression on the RHSs in addition to expanding the body expressions. To handle letrec-values in the same clause, however, we’ll have to get a little more creative.

                                        So far, we haven’t actually tapped very far into syntax/parse’s pattern language over the course of these two blog posts. The full language available to patterns is rather extensive, and we can take advantage of that to write a modification of the above clause that handles both let-values and letrec-values at once:

                                        [({~or {~and head:let-values {~bind [rec? #f]}}
                                        +       {~and head:letrec-values {~bind [rec? #t]}}}
                                        +  ~! ([(x:id ...) rhs] ...) body ...)
                                        + #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +       (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx)]
                                        + #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...])
                                        + #:with [rhs* ...] (if (attribute rec?)
                                        +                       (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                         (map expand-expression (attribute rhs)))
                                        +                       (map expand-expression (attribute rhs)))
                                        + #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                      (map expand-expression (attribute body)))
                                        + (syntax/loc/props this-syntax
                                        +   (head ([(x* ...) rhs*] ...) body* ...))]

                                        The ~bind pattern allows us to explicitly control how attributes are bound as part of the pattern-matching process, which allows us to track when we want to enable the recursive binding behavior of letrec-values in our handler code. Since the vast majority of the logic is otherwise identical, this is a significant improvement over duplicating the clause.

                                        Adding support for letrec-syntaxes+values is done in the same general way, but the pattern is even more involved. In addition to tracking whether or not the bindings are recursive, we have to track if any syntax bindings were present at all, and if they were, bind them with syntax-local-bind-syntaxes:

                                        [({~or {~or {~and head:let-values ~! {~bind [rec? #f] [stxs? #f]}}
                                        +            {~and head:letrec-values ~! {~bind [rec? #t] [stxs? #f]}}}
                                        +       {~seq head:letrec-syntaxes+values {~bind [rec? #t] [stxs? #t]}
                                        +             ~! ([(x/s:id ...) rhs/s] ...)}}
                                        +  ([(x:id ...) rhs] ...) body ...)
                                        + #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +       (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx)
                                        +       (when (attribute stxs?)
                                        +         (for ([xs/s (in-list (attribute x/s))]
                                        +               [rhs/s (in-list (attribute rhs/s))])
                                        +           (syntax-local-bind-syntaxes xs/s rhs/s intdef-ctx)))]
                                        + #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...])
                                        + #:with [rhs* ...] (if (attribute rec?)
                                        +                       (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                         (map expand-expression (attribute rhs)))
                                        +                       (map expand-expression (attribute rhs)))
                                        + #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                      (map expand-expression (attribute body)))
                                        + (if (attribute stxs?)
                                        +     (~> (syntax/loc this-syntax
                                        +           (letrec-values ([(x* ...) rhs*] ...) body* ...))
                                        +         (syntax-track-origin this-syntax #'head))
                                        +     (syntax/loc/props this-syntax
                                        +       (head ([(x* ...) rhs*] ...) body* ...)))]

                                        This behemoth clause handles all three varieties of let forms that can appear in the result of local-expand. Notably, in the letrec-syntaxes+values case, we expand into letrec-values, since the transformer bindings are effectively erased, and we use syntax-track-origin to record that the result originally came from a use of letrec-syntaxes+values.

                                        With these five clauses, we’ve handled all the special forms that can appear in expression position in Racket’s kernel language. To tie things off, we just need to handle the cases of a variable reference, which is represented by a bare identifier not bound to syntax, or literal data, like numbers or strings. We can add one more clause at the end to handle those:

                                        [_
                                        + this-syntax]

                                        Putting them all together, our expand-expression function looks as follows:

                                        (begin-for-syntax
                                        +  (define (expand-expression stx)
                                        +    (syntax-parse (parameterize ([current-context 'expression])
                                        +                    (current-expand stx))
                                        +      #:literal-sets [kernel-literals]
                                        +      [({~or quote quote-syntax #%top #%variable-reference} ~! . _)
                                        +       this-syntax]
                                        +
                                        +      [({~and head {~or #%expression #%plain-app begin begin0 if with-continuation-mark}} ~! form ...)
                                        +       #:with [form* ...] (map expand-expression (attribute form))
                                        +       (syntax/loc/props this-syntax
                                        +         (head form* ...))]
                                        +
                                        +      [(head:#%plain-lambda ~! . clause:lambda-clause)
                                        +       (syntax/loc/props this-syntax
                                        +         (head . clause.expansion))]
                                        +
                                        +      [(head:case-lambda ~! clause:lambda-clause ...)
                                        +       (syntax/loc/props this-syntax
                                        +         (head clause.expansion ...))]
                                        +
                                        +      [({~or {~or {~and head:let-values ~! {~bind [rec? #f] [stxs? #f]}}
                                        +                  {~and head:letrec-values ~! {~bind [rec? #t] [stxs? #f]}}}
                                        +             {~seq head:letrec-syntaxes+values {~bind [rec? #t] [stxs? #t]}
                                        +                   ~! ([(x/s:id ...) rhs/s] ...)}}
                                        +        ([(x:id ...) rhs] ...) body ...)
                                        +       #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +             (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx)
                                        +             (when (attribute stxs?)
                                        +               (for ([xs/s (in-list (attribute x/s))]
                                        +                     [rhs/s (in-list (attribute rhs/s))])
                                        +                 (syntax-local-bind-syntaxes xs/s rhs/s intdef-ctx)))]
                                        +       #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...])
                                        +       #:with [rhs* ...] (if (attribute rec?)
                                        +                             (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                               (map expand-expression (attribute rhs)))
                                        +                             (map expand-expression (attribute rhs)))
                                        +       #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                            (map expand-expression (attribute body)))
                                        +       (if (attribute stxs?)
                                        +           (~> (syntax/loc this-syntax
                                        +                 (letrec-values ([(x* ...) rhs*] ...) body* ...))
                                        +               (syntax-track-origin this-syntax #'head))
                                        +           (syntax/loc/props this-syntax
                                        +             (head ([(x* ...) rhs*] ...) body* ...)))]
                                        +
                                        +      [_
                                        +       this-syntax])))

                                        If we try it out, we’ll see that it really does work! Even complicated local binding forms are handled properly by our expander:

                                        > (expand-expression
                                        +   #'(let ([x 42])
                                        +       (letrec-syntax ([y (make-rename-transformer #'z)]
                                        +                       [z (make-rename-transformer #'x)])
                                        +         (+ y 3))))
                                        +#<syntax (let-values (((x) '42))
                                        +           (letrec-values ()
                                        +             (#%plain-app + x '3)))>

                                        We are now able to expand arbitrary Racket expressions in the same way that the expander does. While this might not seem immediately useful—after all, we haven’t actually gained anything here over just calling local-expand with an empty stop list—we can use this as the basis of an expander that can extensibly handle custom core forms, which I may cover in a future blog post.

                                        Adding support for internal definitions

                                        In the previous section, we defined an expander that could expand arbitrary Racket expressions, but our expander is still imperfect: we still do not support internal definitions. For all forms that have bodies, including #%plain-lambda, case-lambda, let-values, letrec-values, and letrec-syntaxes+values, Racket permits the use of internal definitions.

                                        In practice, internal-definition contexts allow for an increased degree of modularity compared to traditional local binding forms, since they provide an extensible binding language. Users may mix many different binding forms within a single definition context, such as define, define-syntax, match-define, and even struct. However, this means the rewriting process described earlier in this blog post is not as simple as detecting the definitions and lifting them into a local binding form, since it’s not immediately apparent which forms are binding forms and which are expressions!

                                        For this reason, expanding internal-definition contexts happens to be a nontrivial problem in itself. It involves a little more care than expanding expressions does, since it requires using partial expansion to discover which forms are definitions and which forms are expressions. We must take care to never expand too much, but also to expand enough that we reveal all uses of define-values and define-syntaxes (which all definition forms eventually expand into). We also must handle the splicing behavior of begin, which is necessary to allow single forms to expand into multiple definitions.

                                        We’ll start by writing an expand-body function, which operates similarly to our previous expand-expression function. Unlike expand-expression, expand-body will accept a list of syntax objects, which represents the sequence of forms that make up the body. Logically, each body will create a first-class definition context with syntax-local-make-definition-context to represent the sequence of definitions:

                                        (begin-for-syntax
                                        +  (define (expand-body stxs)
                                        +    (define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +    (parameterize ([current-context (list (gensym))]
                                        +                   [current-intdef-ctx intdef-ctx])
                                        +      )))

                                        The bulk of our expand-body function will be a loop that partially expands body forms, adds definitions to the definition context as it discovers them, and returns the expressions and runtime definitions to be rewritten into binding pairs for a letrec-values form. Additionally, the loop will also track so-called disappeared uses and disappeared bindings, which are attached to the expansion using syntax properties to allow tools like DrRacket to learn about the binding structure of phase 1 definitions that are erased as part of macroexpansion.

                                        The skeleton of this loop is relatively straightforward to write. We will iterate over the syntax objects that make up the body, expand them, and process the expansion using syntax-parse:

                                        (begin-for-syntax
                                        +  (define (expand-body stxs)
                                        +    (define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +    (parameterize ([current-context (list (gensym))]
                                        +                   [current-intdef-ctx intdef-ctx])
                                        +      (define-values [binding-clauses exprs disappeared-uses disappeared-bindings]
                                        +        (let loop ([stxs stxs]
                                        +                   [binding-clauses '()]
                                        +                   [exprs '()]
                                        +                   [disappeared-uses '()]
                                        +                   [disappeared-bindings '()])
                                        +          (if (empty? stxs)
                                        +              (values (reverse binding-clauses) (reverse exprs) disappeared-uses disappeared-bindings)
                                        +              (syntax-parse (current-expand (first stxs))
                                        +                #:literal-sets [kernel-literals]
                                        +                )))))))

                                        The hard part, of course, is actually handling the potential results of that expansion. We need to handle three forms specially: begin, define-values, and define-syntaxes. All other results of partial expansion will be treated as expressions. We’ll start by handling begin, since it’s the simplest case; we only need to prepend the subforms to the list of body forms to be processed, then continue looping:

                                        [(head:begin ~! form ...)
                                        + (loop (append (attribute form) stxs) binding-clauses exprs
                                        +       disappeared-uses disappeared-bindings)]

                                        However, as is often the case, this isn’t quite perfect, since the information that these forms came from a surrounding begin is lost, which tools like DrRacket want to know. To solve this problem, the expander adjusts the origin property for all spliced forms, which we can mimic using syntax-track-origin:

                                        [(head:begin ~! form ...)
                                        + (loop (append (for/list ([form (in-list (attribute form))])
                                        +                 (syntax-track-origin form this-syntax #'head))
                                        +               stxs)
                                        +       binding-clauses exprs disappeared-uses disappeared-bindings)]

                                        This is sufficient for begin, so we can move onto the actual definitions themselves. This actually isn’t too hard, since we just need to add the bindings we discover to the first-class definition context and preserve define-values bindings as binding pairs:

                                        [(head:define-values ~! [x:id ...] rhs)
                                        + #:do [(syntax-local-bind-syntaxes (attribute x) #f intdef-ctx)]
                                        + (loop (rest stxs) (cons #'[(x ...) rhs] binding-clauses) exprs
                                        +       disappeared-uses disappeared-bindings)]

                                        This solution is missing one thing, however, which is the use of syntax-local-identifier-as-binding to any use-site scopes that were added to the binding identifier while expanding the binding form in the definition context. Explaining precisely why this is necessary is outside the scope of this blog post, and is best understood by reading the section on use-site scopes in the paper that describes the theory behind Racket’s current macro system, Bindings as Sets of Scopes. In any case, the impact on our implementation is small:

                                        [(head:define-values ~! [x:id ...] rhs)
                                        + #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x))
                                        + #:do [(syntax-local-bind-syntaxes (attribute x*) #f intdef-ctx)]
                                        + (loop (rest stxs) (cons #'[(x* ...) rhs] binding-clauses) exprs
                                        +       disappeared-uses disappeared-bindings)]

                                        Finally, as with begin, we want to track that the binding pairs we generate actually came from a use of define-values (which in turn likely came from a use of some other definition form). Therefore, we’ll add another use of syntax-track-origin to copy and extend the necessary properties:

                                        [(head:define-values ~! [x:id ...] rhs)
                                        + #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x))
                                        + #:do [(syntax-local-bind-syntaxes (attribute x*) #f intdef-ctx)]
                                        + (loop
                                        +  (rest stxs)
                                        +  (cons (syntax-track-origin #'[(x* ...) rhs] this-syntax #'head) binding-clauses)
                                        +  exprs disappeared-uses disappeared-bindings)]

                                        That’s it for define-values. All that’s left is to handle define-syntaxes, which is quite similar, but instead of storing the definition in a binding pair, its RHS is immediately evaluated and added to the definition context using syntax-local-bind-syntaxes:

                                        [(head:define-syntaxes ~! [x:id ...] rhs)
                                        + #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x))
                                        + #:do [(syntax-local-bind-syntaxes (attribute x*) #'rhs intdef-ctx)]
                                        + (loop (rest stxs) binding-clauses exprs
                                        +       (cons #'head disappeared-uses) (cons (attribute x*) disappeared-bindings))]

                                        As the above snippet indicates, this is also where the disappeared uses and disappeared bindings come in. In previous cases, we’ve used syntax-track-origin to indicate that a piece of syntax was the result of expanding a different piece of syntax, but in this case, define-syntaxes doesn’t expand into anything at all; it’s simply removed from the expansion entirely. Therefore, we need to resort to tracking the information in syntax properties on the resulting letrec-values form, so we’ll save them for later.

                                        Finally, to finish things up, we can add a catchall clause that handles all other forms, which are now guaranteed to be expressions:

                                        [_
                                        + (loop (rest stxs) binding-clauses (cons this-syntax exprs)
                                        +       disappeared-uses disappeared-bindings)]

                                        This completes our loop that processes definition forms, so all that’s left to do is handle the results. The only significant remaining work is to actually expand the RHSs of the binding pairs we collected and the body expressions, which can be done by calling our own expand-expression function directly:

                                        (define expanded-binding-clauses
                                        +  (for/list ([binding-clause (in-list binding-clauses)])
                                        +    (syntax-parse binding-clause
                                        +      [[(x ...) rhs]
                                        +       (quasisyntax/loc/props this-syntax
                                        +         [(x ...) #,(expand-expression #'rhs)])])))
                                        +(define expanded-exprs (map expand-expression exprs))

                                        Finally, we can assemble all the pieces together into a single local binding form with the appropriate syntax properties:

                                        (~> #`(letrec-values #,expanded-binding-clauses #,@expanded-exprs)
                                        +    (syntax-property 'disappeared-uses disappeared-uses)
                                        +    (syntax-property 'disappeared-bindings disappeared-bindings))

                                        That’s it. We’ve now written an expand-body function that can process internal definition contexts in the same way that the macroexpander does. Overall, the whole function is just under 45 lines of code:

                                        (begin-for-syntax
                                        +  (define (expand-body stxs)
                                        +    (define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +    (parameterize ([current-context (list (gensym))]
                                        +                   [current-intdef-ctx intdef-ctx])
                                        +      (define-values [binding-clauses exprs disappeared-uses disappeared-bindings]
                                        +        (let loop ([stxs stxs]
                                        +                   [binding-clauses '()]
                                        +                   [exprs '()]
                                        +                   [disappeared-uses '()]
                                        +                   [disappeared-bindings '()])
                                        +          (if (empty? stxs)
                                        +              (values (reverse binding-clauses) (reverse exprs) disappeared-uses disappeared-bindings)
                                        +              (syntax-parse (current-expand (first stxs))
                                        +                #:literal-sets [kernel-literals]
                                        +                [(head:begin ~! form ...)
                                        +                 (loop (append (for/list ([form (in-list (attribute form))])
                                        +                                 (syntax-track-origin form this-syntax #'head))
                                        +                               stxs)
                                        +                       binding-clauses exprs disappeared-uses disappeared-bindings)]
                                        +                [(head:define-values ~! [x:id ...] rhs)
                                        +                 #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x))
                                        +                 #:do [(syntax-local-bind-syntaxes (attribute x*) #f intdef-ctx)]
                                        +                 (loop
                                        +                  (rest stxs)
                                        +                  (cons (syntax-track-origin #'[(x* ...) rhs] this-syntax #'head) binding-clauses)
                                        +                  exprs disappeared-uses disappeared-bindings)]
                                        +                [(head:define-syntaxes ~! [x:id ...] rhs)
                                        +                 #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x))
                                        +                 #:do [(syntax-local-bind-syntaxes (attribute x*) #'rhs intdef-ctx)]
                                        +                 (loop (rest stxs) binding-clauses exprs
                                        +                       (cons #'head disappeared-uses) (cons (attribute x*) disappeared-bindings))]
                                        +                [_
                                        +                 (loop (rest stxs) binding-clauses (cons this-syntax exprs)
                                        +                       disappeared-uses disappeared-bindings)]))))
                                        +      (define expanded-binding-clauses
                                        +        (for/list ([binding-clause (in-list binding-clauses)])
                                        +          (syntax-parse binding-clause
                                        +            [[(x ...) rhs]
                                        +             (quasisyntax/loc/props this-syntax
                                        +               [(x ...) #,(expand-expression #'rhs)])])))
                                        +      (define expanded-exprs (map expand-expression exprs))
                                        +      (~> #`(letrec-values #,expanded-binding-clauses #,@expanded-exprs)
                                        +          (syntax-property 'disappeared-uses disappeared-uses)
                                        +          (syntax-property 'disappeared-bindings disappeared-bindings)))))

                                        The next step is to actually use this function. We need to replace certain recursive calls to expand-expression with calls to expand-body, but if we do this naïvely, we’ll have some problems. Currently, when we expand body forms, they’re always immediately inside another definition context (i.e. the bindings introduced by lambda formals or by let binding pairs), but they haven’t actually been expanded in that context yet. When we call expand-body, we create a nested context, which will inherit the bindings, but won’t automatically add the parent context’s scope. Therefore, we need to manually call internal-definition-context-introduce on the body syntax objects before calling expand-body. We can write a small helper function to make this easier:

                                        (begin-for-syntax
                                        +  (define (expand-body/in-ctx stxs ctx)
                                        +    (define (add-ctx-scope stx)
                                        +      (internal-definition-context-introduce ctx stx 'add))
                                        +    (parameterize ([current-intdef-ctx ctx])
                                        +      (add-ctx-scope (expand-body (map add-ctx-scope stxs))))))

                                        Now we just need to replace the relevant calls to expand-expression with calls to expand-body/in-ctx, starting with a minor adjustment to our lambda-clause syntax class from earlier:

                                        (begin-for-syntax
                                        +  (define-syntax-class lambda-clause
                                        +    #:description #f
                                        +    #:attributes [expansion]
                                        +    #:commit
                                        +    [pattern [formals:plain-formals body ...]
                                        +             #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +                   (syntax-local-bind-syntaxes (attribute formals.id) #f intdef-ctx)]
                                        +             #:with formals* (internal-definition-context-introduce intdef-ctx #'formals)
                                        +             #:with body* (expand-body/in-ctx (attribute body) intdef-ctx)
                                        +             #:attr expansion #'[formals* body*]]))

                                        The only other change must occur in the handling of the various let forms, which similarly replaces expand-expression with expand-body/in-ctx:

                                        [({~or {~or {~and head:let-values ~! {~bind [rec? #f] [stxs? #f]}}
                                        +            {~and head:letrec-values ~! {~bind [rec? #t] [stxs? #f]}}}
                                        +       {~seq head:letrec-syntaxes+values {~bind [rec? #t] [stxs? #t]}
                                        +             ~! ([(x/s:id ...) rhs/s] ...)}}
                                        +  ([(x:id ...) rhs] ...) body ...)
                                        + #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx)))
                                        +       (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx)
                                        +       (when (attribute stxs?)
                                        +         (for ([xs/s (in-list (attribute x/s))]
                                        +               [rhs/s (in-list (attribute rhs/s))])
                                        +           (syntax-local-bind-syntaxes xs/s rhs/s intdef-ctx)))]
                                        + #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...])
                                        + #:with [rhs* ...] (if (attribute rec?)
                                        +                       (parameterize ([current-intdef-ctx intdef-ctx])
                                        +                         (map expand-expression (attribute rhs)))
                                        +                       (map expand-expression (attribute rhs)))
                                        + #:with body* (expand-body/in-ctx (attribute body) intdef-ctx)
                                        + (if (attribute stxs?)
                                        +     (~> (syntax/loc this-syntax
                                        +           (letrec-values ([(x* ...) rhs*] ...) body*))
                                        +         (syntax-track-origin this-syntax #'head))
                                        +     (syntax/loc/props this-syntax
                                        +       (head ([(x* ...) rhs*] ...) body*)))]

                                        With these changes, we’ve now extended our expression expander with the ability to expand internal definitions. We can see this in action on a simple example:

                                        > (expand-expression
                                        +   #'(let ()
                                        +       (define x 42)
                                        +       (define-syntax y (make-rename-transformer #'z))
                                        +       (define-syntax z (make-rename-transformer #'x))
                                        +       (+ y 3)))
                                        +#<syntax (let-values ()
                                        +           (letrec-values ([(x) '42])
                                        +             (#%app + x '3)))>

                                        Just as we’d like, the transformer bindings were expanded and subsequently eliminated, and the runtime binding was collected into a letrec-values form. The outer let-values is left over from the outer let, which is needed only to create an internal-definition context to hold our internal definitions.

                                        Putting the expression expander to work

                                        So far, we’ve done a lot of work to emulate the behavior of Racket’s macroexpander, and as the above example demonstrates, we’ve been fairly successful in that goal. However, you might be wondering why we did any of this, as replicating the behavior of local-expand is not very useful on its own. As mentioned above, this can be used as the foundation of an expander for custom core forms that extends, rather than replaces, the built-in Racket core forms, It can also be used to “cheat” and expand through the behavior of the local-expand stop list, which implicitly adds the Racket core forms to any non-empty stop list. Hopefully, I’ll have a chance to cover some of these things more deeply in the future, but for now, I’ll just give a small taste of the latter.

                                        By using the power of our expand-expression function, it’s actually possible to use this kind of expression expander to do genuinely nefarious things, such as hijack the behavior of arbitrary macros! For example, we could do something evil like make for loops run in reverse order by adding for to current-stop-list, then adding an additional special case to expand-expression for for:

                                        (begin-for-syntax
                                        +  (define current-stop-list (make-parameter (list #'define-values #'define-syntaxes #'for)))
                                        +
                                        +  (define (expand-expression stx)
                                        +    (syntax-parse (parameterize ([current-context 'expression])
                                        +                    (current-expand stx))
                                        +      #:literal-sets [kernel-literals]
                                        +      #:literals [for]
                                        +      ; ...
                                        +      [(head:for ([x:id seq:expr] ...) body ...+)
                                        +       (syntax/loc/props this-syntax
                                        +         (head ([x (in-list (reverse (sequence->list seq)))] ...)
                                        +           body ...))]
                                        +      ; ...
                                        +    )))

                                        Amazingly, due to the fact that we’ve taken complete control of the expansion process, this will rewrite uses of for even if they are introduced by macroexpansion. For example, we could write a small macro that expands into a use of for:

                                        (define-simple-macro (print-up-to n)
                                        +  (for ([i (in-range n)])
                                        +    (println i)))
                                        +
                                        +> (print-up-to 5)
                                        +0
                                        +1
                                        +2
                                        +3
                                        +4

                                        If we write a wrapper macro that applies our evil version of expand-expression to its body, then wrap a use of our print-up-to macro with it, it will execute the loop in reverse order:

                                        (define-syntax-parser hijack-for-loops
                                        +  [(_ form:expr) (expand-expression #'form)])
                                        +
                                        +> (hijack-for-loops
                                        +   (print-up-to 5))
                                        +4
                                        +3
                                        +2
                                        +1
                                        +0

                                        On its own, this is not that impressive, since we could have just used local-expand on the body directly to achieve this. However, what’s remarkable about hijack-for-loops is that it will work even if the for loop is buried deep inside some arbitrary expression:

                                        > (define foo
                                        +    (hijack-for-loops
                                        +     (lambda (x)
                                        +       (define n (* x 2))
                                        +       (print-up-to n))))
                                        +> (foo 3)
                                        +5
                                        +4
                                        +3
                                        +2
                                        +1
                                        +0

                                        Of course, this example is rather contrived—mucking with for loops like this isn’t useful at all, and nobody would really write print-up-to as a macro, anyway—but there is potential for using this technique to do more interesting things.

                                        Closing thoughts

                                        The system outlined in this blog post is not something I would recommend using in any real macro. It is enormously complicated, requires knowledge well above that of your average working macrologist, and it involves doing rather horrible things to the macro system, things it was undoubtably never designed to do. Still, I believe this blog post is useful, for a few different reasons:

                                        1. The technology outlined in this post, while perhaps not directly applicable to existing real-world problems, provides a framework for implementing various new kinds of syntax transformations in Racket without extending the macro system. It demonstrates the expressive power of the macro system, and it hopefully lays the foundation for a better, more high-level interface for users who wish to define their own languages with custom core forms.

                                        2. This system provides insight into the way the Racket macroexpander operates, in terms of the userspace syntax API. The canonical existing model of hygienic macroexpansion, in the aforementioned Bindings as Sets of Scopes paper, does not explain the workings of internal definition contexts in detail, and it certainly doesn’t explain them in terms that a Racket programmer would already be familiar with. By reencoding those ideas within the macro system itself, an advanced macro writer may be able to more easily connect concepts in the macro system’s implementation to concepts they have already been exposed to.

                                        3. The capability of the proof-of-concept outlined here demonstrates that the limitation imposed by the existing implementation of the stop list (namely, the way it is implicitly extended with additional identifiers) is essentially artificial, and it can be hacked around with sufficient (albeit significant) effort. This isn’t enormously important, but it is somewhat relevant to a recent debate in a GitHub issue about the handling of the local-expand stop list.

                                        4. Finally, for myself as much as anyone else, this implementation records in a concise way (perhaps overly concise at times) the collection of very subtle details I’ve learned over the past six months about how information is preserved and propagated during the expansion process.

                                        This blog post is not for everybody. If you made it to the end, give yourself a pat on the back. If you made it to the end and understood everything you read: congratulations, you are a certified expert in Racket macro programming. If not, do not fear, and do not lose hope—I plan for something significantly more mellow next time.

                                        As always, I’d like to give thanks to the people who contributed significantly, if indirectly, to the contents of this blog post, namely Matthew Flatt, Michael Ballantyne, and Ryan Culpepper. And finally, for those interested, all of the code in this blog post can be found in a runnable form in this GitHub gist.

                                          \ No newline at end of file diff --git a/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/index.html b/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/index.html new file mode 100644 index 0000000..0dc2930 --- /dev/null +++ b/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/index.html @@ -0,0 +1,106 @@ +Macroexpand anywhere with local-apply-transformer!

                                          Macroexpand anywhere with local-apply-transformer!

                                          ⦿ racket, macros

                                          Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only certain positions in Racket code are subject to macroexpansion.

                                          To illustrate, consider a macro that provides a Clojure-style let form:

                                          (require syntax/parse/define)
                                          +
                                          +(define-simple-macro (clj-let [{~seq x:id e:expr} ...] body:expr ...+)
                                          +  (let ([x e] ...) body ...))

                                          This can be used anywhere an expression is expected, and it does as one would expect:

                                          > (clj-let [x 1
                                          +            y 2]
                                          +    (+ x y))
                                          +3

                                          However, a novice macro programmer might realize that clj-let really only modifies the syntax of binding pairs for a let form. Therefore, could one define a macro that only adjusts the binding pairs of some existing let form instead of expanding to an entire let? That is, could one write the above example like this:

                                          (define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...])
                                          +  ([x e] ...))
                                          +
                                          +> (let (clj-binding-pairs
                                          +        [x 1
                                          +         y 2])
                                          +    (+ x y))
                                          +3

                                          The answer is no: the binding pairs of a let form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion anywhere in a Racket program.

                                          Why only some positions are subject to macroexpansion

                                          To understand why the macroexpander refuses to touch certain positions in a program, we must first understand how the macro system operates. In Racket, a macro is defined as a compile-time function associated with a particular binding, and macros are given complete control over the syntax trees they are surrounded with. If we define a macro mac, then we write the expression (mac form), form is provided as-is to mac as a syntax object. Its structure can be anything at all, since mac can be an arbitrary Racket function, and that function can use form however it pleases.

                                          To give a concrete illustration, consider a macro that binds some identifiers to symbols in a local scope:

                                          (define-simple-macro (let-symbols (x:id ...) body ...+)
                                          +  (let ([x 'x] ...) body ...))
                                          +
                                          +> (let-symbols (hello goodbye)
                                          +    (list hello goodbye))
                                          +'(hello goodbye)

                                          It isn’t the most exciting macro in the world, but it illustrates a key point: the first subform to let-symbols is a list of identifiers that are eventually put in binding position. This means that hello and goodbye are bindings, not uses, and such bindings shadow any existing bindings that might have been in scope:

                                          > (let ([foo 42])
                                          +    (let-symbols (foo)
                                          +      foo))
                                          +'foo

                                          This might not seem very interesting, but it’s critical to understand, since it means that the expander can’t know which sub-pieces of a use of let-symbols will eventually be expressions themselves until it expands the macro and discovers it produces a let form, so it can’t know where it’s safe to perform macroexpansion. To make this more explicit, imagine we define a macro under some name, then try and use that name with our let-symbols macro:

                                          (define-simple-macro (hello x:id)
                                          +  (x:id))
                                          +
                                          +> (let-symbols (hello goodbye)
                                          +    hello)

                                          What should the above program do? If we treat the first use of hello in the let-symbols form as a macro application, then (hello goodbye) should be transformed into (goodbye), and the use of hello in the body should be a syntax error. But if the first use of hello was instead intended to be a binder, then it should shadow the hello definition above, and the output of the program should be 'hello.

                                          To avoid the chaos that would ensue if defining a macro could completely break local reasoning about other macros, Racket chooses the second option, and the program produces 'hello. The macroexpander has no way of knowing how each macro will inspect its constituent pieces, so it avoids touching anything until the macro expands. After it discovers the let form in the expansion of let-symbols, it can safely determine that the body expressions are, indeed, expressions, and it can recursively expand any macros they contain. To put things another way, a macro’s sub-forms are never expanded before the macro itself is expanded, only after.

                                          Forcing sub-form expansion

                                          The above section explains why the expander must operate as it does, but it’s a little bit unsatisfying. What if we write a macro where we want certain sub-forms to be expanded before they are passed to us? Fortunately, the Racket macro system provides an API to handle this use case, too.

                                          It is true that the Racket macro system never automatically expands sub-forms before outer forms are expanded, but macro transformers can explicitly op-in to recursive expansion via the local-expand function. This function effectively yields control back to the expander to expand some arbitrary piece of syntax as an expression, and when it returns, the macro transformer can inspect the expanded expression however it wishes. In theory, this can be used to implement extensible macros that allow macroexpansion in locations other than expression position.

                                          To give an example of such a macro, consider the Racket match form, which implements an expressive pattern-matcher as a macro. One of the most interesting qualities of Racket’s match macro is that its pattern language is user-extensible, essentially allowing pattern-level macros. For example, a user might find they frequently match against natural numbers, and they wish to be able to write (nat n) as a shorthand for (? exact-nonnegative-integer? n). Fortunately, this is easy using define-match-expander:

                                          (define-match-expander nat
                                          +  (syntax-parser
                                          +    [(_ pat)
                                          +     #'(? exact-nonnegative-integer? pat)]))
                                          +
                                          +> (match '(-5 -2 4 -7)
                                          +    [(list _ ... (nat n) _ ...)
                                          +     n])
                                          +4

                                          Clearly, match is somehow expanding the nat match expander as a part of its expansion. Is it using local-expand?

                                          Well, no. While a previous blog post of mine has illustrated that it is possible to do such a thing with local-expand via some clever trickery, local-expand is really designed to expand expressions. This is a problem, since (nat n) is not an expression, it’s a pattern: it will expand into (? exact-nonnegative-integer? n), which will lead to a syntax error, since ? is not bound in the world of expressions. Instead, for a long while, match and forms like it have emulated how the expander performs macroexpansion in ad-hoc ways. Fortunately, as of Racket v7.0, the new local-apply-transformer API provides a way to invoke recursive macroexpansion in a consistent way, and it doesn’t assume that what’s being expanded is an expression.

                                          A closer look at local-apply-transformer

                                          If local-apply-transformer is the answer, what does it actually do? Well, local-apply-transformer allows explicitly invoking a transformer function on some piece of syntax and retrieving the result. In other words, local-apply-transformer allows expanding an arbitrary macro, but since it doesn’t make any assumptions about what the output will be, it only expands it once: just a single step of macro transformation.

                                          To illustrate, we can write a macro that uses local-apply-transformer to invoke a transformer function and preserve the result using quote-syntax:

                                          (require (for-syntax syntax/apply-transformer))
                                          +
                                          +(define-for-syntax flip
                                          +  (syntax-parser
                                          +    [(a b more ...)
                                          +     #'(b a more ...)]))
                                          +
                                          +(define-simple-macro (mac)
                                          +  #:with result (local-apply-transformer flip #'(([x 1]) let x) 'expression)
                                          +  (quote-syntax result))

                                          When we use mac, our flip function will be applied, as a macro, to the syntax object we provide:

                                          > (mac)
                                          +#<syntax (let ((x 1)) x)>

                                          Alright, so this works, but it raises some questions. Why is flip defined as a function at phase 1 (using define-for-syntax) instead of as a macro (using define-syntax)? What’s the deal with the 'expression argument to local-apply-transformer given that local-apply-transformer is supposedly decoupled from expression expansion? And finally, how is this any different from just calling our flip function on the syntax object directly by writing (flip #'(([x 1]) let x))?

                                          Let’s start with the first of those questions: why is flip defined as a function rather than as a macro? Well, local-apply-transformer is a fairly low-level operation: remember, it doesn’t assume anything about the argument it’s given! Therefore, it doesn’t take an expression containing a macro and expand it based on its structure, it needs to be explicitly provided the macro transformer function to apply. In practice, this might not seem very useful, since presumably we want to write our macros as macros, not as phase 1 functions. Fortunately, it’s possible to look up the function associated with a macro binding using the syntax-local-value function, so if we use that, we can define flip using define-syntax as usual:

                                          (define-syntax flip
                                          +  (syntax-parser
                                          +    [(a b more ...)
                                          +     #'(b a more ...)]))
                                          +
                                          +(define-simple-macro (mac)
                                          +  #:with result (local-apply-transformer (syntax-local-value #'flip)
                                          +                                         #'(([x 1]) let x)
                                          +                                         'expression)
                                          +  (quote-syntax result))

                                          Now for the next question: what is the meaning of the 'expression argument? This one is more of a historical artifact than anything else: when the expander applies a macro transformer, it does it in a “context”, which is accessible using the syntax-local-context function. This context can be one of a predefined enumeration of cases, including 'expression, 'top-level, 'module, 'module-begin, or a list representing a definition context. Whether or not any of those actually apply to our use case, we still have to pick one, but aside from how they affect the value returned by syntax-local-context (which some macros inspect), the value we choose is largely irrelevant. Using 'expression will do, even if it’s a bit of a lie.

                                          Finally, how does any of this differ from just applying the function we get directly? Well, the critical answer is all about hygiene. Racket’s macro system is hygienic, which, among other things, ensures bindings defined with the same name in different places do not unintentionally conflict. Racket’s hygiene mechanism is implemented in the macroexpander, when macro transformers are applied. If we just applied the flip transformer procedure to a syntax object directly, we would circumvent this hygiene mechanism, potentially causing all sorts of problems. By using local-apply-transformer, we ensure hygiene is preserved.

                                          There is one small problem left with our program, however. Can you spot it? The key is to consider what would happen if we used flip as an ordinary macro, without using local-apply-transformer:

                                          > (flip (([x 1]) let x))
                                          +let: bad syntax
                                          +  in: let

                                          What happened? Well, remember that when a macro in Racket is used, it receives the whole use site as a syntax object: in this case, #'(flip (([x 1]) let x)). This means that flip ought to be written to parse its argument slightly differently:

                                          (define-syntax flip
                                          +  (syntax-parser
                                          +    [(_ (a b more ...))
                                          +     #'(b a more ...)]))

                                          Indeed, now that we’ve properly restructured the macro, we can easily switch to using the convenient define-simple-macro shorthand:

                                          (define-simple-macro (flip (a b more ...))
                                          +  (b a more ...))

                                          This means we also need to update our definition of mac to provide the full syntax object the expander would:

                                          (define-simple-macro (mac)
                                          +  #:with result (local-apply-transformer (syntax-local-value #'flip)
                                          +                                         #'(flip (([x 1]) let x))
                                          +                                         'expression)
                                          +  (quote-syntax result))

                                          This might seem redundant, but remember, local-apply-transformer is very low-level! While the convention that (mac . _) is the syntax for a macro transformation might seem obvious, local-apply-transformer makes no assumptions. It just does what we tell it to do.

                                          Applying local-apply-transformer

                                          So what does local-apply-transformer have to do with the problem at the beginning of this blog post? Well, as it happens, we can use local-apply-transformer to implement a macro that allows expansion anywhere using some simple tricks. While it’s true that we cannot magically divine which locations ought to be expanded, what we can do is explicitly annotate which places to expand.

                                          To do this, we will implement a macro, expand-inside, that looks for subforms annotated with a special $expand identifier and performs macro transformation on those locations before proceeding with ordinary macroexpansion. Using the clj-binding-pairs example from the beginning of this blog post, our solution to that problem will look like this:

                                          (define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...])
                                          +  ([x e] ...))
                                          +
                                          +> (expand-inside
                                          +   (let ($expand
                                          +         (clj-binding-pairs
                                          +          [x 1
                                          +           y 2]))
                                          +     (+ x y)))
                                          +3

                                          Put another way, expand-inside will force eager expansion on any subform surrounded with an $expand annotation.

                                          We’ll start by defining the $expand binding itself. This binding won’t mean anything at all outside of expand-inside, but we’d like it to be a unique binding so that users can rename it (using, rename-in, for example) if they wish. To do this, we’ll use the usual trick of defining it as a macro that always produces an error if it’s ever used:

                                          (define-syntax ($expand stx)
                                          +  (raise-syntax-error #f "illegal outside an ‘expand-inside’ form" stx))

                                          Next, we’ll implement a syntax class that will form the bulk of our implementation of expand-inside. Since we need to find uses of $expand that might be deeply-nested inside the syntax object provided to expand-inside, we need to recursively look through the syntax object, find any instances of $expand, and put it all back together once we’re done. This can be done relatively cleanly using a recursive syntax class:

                                          (begin-for-syntax
                                          +  (define-syntax-class do-expand-inside
                                          +    #:literals [$expand]
                                          +    #:attributes [expansion]
                                          +    [pattern {~or $expand ($expand . _)}
                                          +             #:with :do-expand-inside (do-$expand this-syntax)]
                                          +    [pattern (a:do-expand-inside . b:do-expand-inside)
                                          +             #:attr expansion
                                          +             (let ([reassembled (cons (attribute a.expansion)
                                          +                                      (attribute b.expansion))])
                                          +               (if (syntax? this-syntax)
                                          +                   (datum->syntax this-syntax reassembled
                                          +                                  this-syntax this-syntax)
                                          +                   reassembled))]
                                          +    [pattern _ #:attr expansion this-syntax]))

                                          There are some tricky details to get right in the reassembly of pairs, since syntax lists are actually composed of ordinary pairs rather than syntax pairs, but ultimately, the code for walking a syntax object is small. The key case of this syntax class is the call to do-$expand in the first clause, which we have not yet defined. This function will actually handle performing the expansion by invoking local-apply-transformer:

                                          (begin-for-syntax
                                          +  (define (do-$expand stx)
                                          +    (syntax-parse stx
                                          +      [(_ {~and form {~or trans (trans . _)}})
                                          +       #:declare trans (static (disjoin procedure? set!-transformer?)
                                          +                               "syntax transformer")
                                          +       (local-apply-transformer (attribute trans.value)
                                          +                                #'form
                                          +                                'expression)])))

                                          This uses the handy static syntax class that comes with syntax/parse, which implicitly handles the call to syntax-local-value and produces a nice error message if the value returned does not match a predicate. All we have to do is apply the transformer value bound to the trans.value attribute using local-apply-transformer, and now the expand-macro can be written in just a couple lines of code:

                                          (define-syntax-parser expand-inside
                                          +  #:track-literals
                                          +  [(_ form:do-expand-inside) #'form.expansion])

                                          (Using the #:track-literals option, also new in Racket v7.0, ensures that Check Syntax will be able to recognize the uses of $expand that disappear from after expand-inside is expanded.)

                                          Putting everything together, our example from above really works:

                                          (define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...])
                                          +  ([x e] ...))
                                          +
                                          +> (expand-inside
                                          +   (let ($expand
                                          +         (clj-binding-pairs
                                          +          [x 1
                                          +           y 2]))
                                          +     (+ x y)))
                                          +3

                                          That’s it. All told, the entire implementation is only about 30 lines of code. For a full, compilable, working example, see this gist.

                                            \ No newline at end of file diff --git a/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/index.html b/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/index.html new file mode 100644 index 0000000..497b8b1 --- /dev/null +++ b/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/index.html @@ -0,0 +1,238 @@ +Defeating Racket’s separate compilation guarantee

                                            Defeating Racket’s separate compilation guarantee

                                            ⦿ racket, macros

                                            Being a self-described programming-language programming language is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate composable and compilable macros. One of the module system’s foundational properties is its separate compilation guarantee, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is essential for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.

                                            Yet today, in this blog post, we’re going to do exactly that.

                                            What is the separate compilation guarantee?

                                            Before we get to the fun part (i.e. breaking things), let’s go over some fundamentals so we understand what we’re breaking. The authoritative source for the separate compilation guarantee is the Racket reference, but it is dense, as authoritative sources tend to be. Although I enjoy reading technical manuals for sport, it is my understanding that not all the people who read this blog are as strange as I am, so let’s start with a quick primer, instead. (If you’re already an expert, feel free to skip to the next section.)

                                            Racket is a macro-enabled programming language. In Racket, a macro is a user-defined, code-to-code transformation that occurs at compile-time. These transformations cannot make arbitrary changes to the program—in Racket, they are usually required to be local, affecting a single expression or definition at a time—but they may be implemented using arbitrary code. This means that a macro can, if it so desires, read the SSH keys off your filesystem and issue an HTTP request to send them someplace.

                                            That kind of attack is bad, admittedly, but it’s also uninteresting: Racket allows you do all that and then some, making no attempt to prevent it.1 Racket calls these “external effects,” things that affect state outside of the programming language. They sound scary, but in practice, internal effects—effects that mutate state inside the programming language—are a much bigger obstacle to practical programming. Let’s take a look at why.

                                            Let’s say we have a module with some global, mutable state. Perhaps it is used to keep track of a set of delicious foods:

                                            ;; foods.rkt
                                            +#lang racket
                                            +(provide delicious-food? add-delicious-food!)
                                            +
                                            +(define delicious-foods (mutable-set))
                                            +
                                            +(define (delicious-food? food)
                                            +  (set-member? delicious-foods food))
                                            +
                                            +(define (add-delicious-food! new-food)
                                            +  (set-add! delicious-foods new-food))

                                            Using this interface, let’s write a program that checks if a particular food, given as a command-line argument, is delicious:

                                            ;; check-food.rkt
                                            +#lang racket
                                            +(require "foods.rkt")
                                            +
                                            +(add-delicious-food! "pineapple")
                                            +(add-delicious-food! "sushi")
                                            +(add-delicious-food! "cheesecake")
                                            +
                                            +(command-line
                                            +  #:args [food-to-check]
                                            +  (if (delicious-food? food-to-check)
                                            +      (printf "~a is a delicious food.\n" food-to-check)
                                            +      (printf "~a is not delicious.\n" food-to-check)))
                                            $ racket check-food.rkt cheesecake
                                            +cheesecake is a delicious food.
                                            +$ racket check-food.rkt licorice
                                            +licorice is not delicious.

                                            Exhilarating. (Sorry, licorice fans.) But what if a macro were to call add-delicious-food!? What would happen? For example, what if we wrote a macro to add a lot of foods at once?2

                                            (require syntax/parse/define)
                                            +(define-simple-macro (add-food-combinations! [fst:string ...]
                                            +                                             [snd:string ...])
                                            +  #:do [(for* ([fst-str (in-list (syntax->datum #'[fst ...]))]
                                            +               [snd-str (in-list (syntax->datum #'[snd ...]))])
                                            +          (add-delicious-food! (string-append fst-str " " snd-str)))]
                                            +  (void))
                                            +
                                            +; should add “fried chicken,” “roasted chicken”, “fried potato,” and “roasted potato”
                                            +(add-food-combinations! ["fried" "roasted"] ["chicken" "potato"])

                                            Now, what do you think executing racket check-food.rkt 'fried chicken' will do?

                                            Clearly, the program should print fried chicken is a delicious food, and indeed, many traditional Lisp systems would happily produce such a result. After all, running racket check-food.rkt 'fried chicken' must load the source code inside check-food.rkt, expand and compile it, then run the result. While the program is being expanded, the compile-time calls to add-delicious-food! should add new elements to the delicious-food set, so when the program is executed, the string "fried chicken" ought to be in it.

                                            But if you actually try this yourself, you will find that isn’t what happens. Instead, Racket rejects the program:

                                            $ racket check-food.rkt 'fried chicken'
                                            +check-food.rkt:12:11: add-delicious-food!: reference to an unbound identifier
                                            +  at phase: 1; the transformer environment
                                            +  in: add-delicious-food!

                                            Why does Racket reject this program? Well, consider that Racket allows programs to be pre-compiled using raco make, doing all the work of macroexpansion and compilation to bytecode ahead of time. Subsequent runs of the program will use the pre-compiled version, without having to run all the macros again. This is a problem, since expanding the add-food-combinations! macro had side-effects that our program depended on!

                                            If Racket allowed the above program, it might do different things depending on whether it was pre-compiled. Running directly from source code might treat 'fried chicken' as a delicious food, while running from pre-compiled bytecode might not. Racket considers this unacceptable, so it disallows the program entirely.

                                            Preserving separate compilation via phases

                                            Hopefully, you are now mostly convinced that the above program is a bad one, but you might have some lingering doubts. You might, for example, wonder if Racket disallows mutable compile-time state entirely. That is not the case—Racket really does allow everything that happens at runtime to happen at compile-time—but it does prevent compile-time and run-time state from ever interacting. Racket stratifies every program into a compile-time part and a run-time part, and it restricts communication between them to limited, well-defined channels (mainly via expanding to code that does something at run-time).

                                            Racket calls this system of stratification phases. Code that executes at run-time belongs to the run-time phase, while code that executes at compile-time (i.e. macros) belongs to the compile-time phase. When a variable is defined, it is always defined in a particular phase, so bindings declared with define can only be used at run-time, while bindings declared with define-for-syntax can only be used at compile-time. Since add-delicious-food! was declared using define, it was not allowed (and in fact was not even visible) in the body of the add-food-combinations! macro.

                                            While the whole macro system could work precisely as just described, such a strict stratification would be incredibly rigid. Since every definition would belong to either run-time or compile-time, but never both, reusing run-time code to implement macros would be impossible. While the example in the previous section might make it seem like that’s a good thing, it very often isn’t: imagine if general-purpose functions like map and filter all needed to be written twice!

                                            To avoid this problem, Racket allows modules to be imported at both run-time and compile-time, so long as it’s done explicitly. Writing (require "some-library.rkt") requires some-library.rkt for run-time code, but writing (require (for-syntax "some-library.rkt")) requires it for compile-time code. Requiring a module for-syntax is sort of like implicitly adjusting all of its uses of define to be define-for-syntax, instead, effectively shifting all the code from run-time to compile-time. This kind of operation is therefore known as phase shifting in Racket terminology.

                                            We can use phase shifting to make the program we wrote compile. If we adjust the require at the beginning of our program, then we can ensure add-delicious-food! is visible to both the run-time and compile-time parts of check-food.rkt:

                                            (require "foods.rkt" (for-syntax "foods.rkt"))

                                            Now our program compiles. However, if you’ve been following everything carefully, you should be wondering why! According to the last section, sharing state between run-time and compile-time fundamentally can’t work without introducing inconsistencies between uncompiled and pre-compiled code. And that’s true—such a thing would cause all sorts of problems, and Racket doesn’t allow it. If you run the program, whether pre-compiled or not, you’ll find it always does the same thing:

                                            $ racket check-food.rkt 'fried chicken'
                                            +fried chicken is not delicious.

                                            This seems rather confusing. What happened to the calls to add-delicious-food! inside our add-food-combinations! macro? If we stick a printf inside add-delicious-food!, we’ll find that it really does get called:

                                            (define (add-delicious-food! new-food)
                                            +  (printf "Registering ~a as a delicious food.\n" new-food)
                                            +  (set-add! delicious-foods new-food))
                                            $ racket check-food.rkt 'fried chicken'
                                            +Registering fried chicken as a delicious food.
                                            +Registering fried potato as a delicious food.
                                            +Registering roasted chicken as a delicious food.
                                            +Registering roasted potato as a delicious food.
                                            +Registering pineapple as a delicious food.
                                            +Registering sushi as a delicious food.
                                            +Registering cheesecake as a delicious food.
                                            +fried chicken is not delicious.

                                            And in fact, if we pre-compile check-food.rkt, we’ll see that the first four registrations appear at compile-time, exactly as we expect:

                                            $ raco make check-food.rkt
                                            +Registering fried chicken as a delicious food.
                                            +Registering fried potato as a delicious food.
                                            +Registering roasted chicken as a delicious food.
                                            +Registering roasted potato as a delicious food.
                                            +$ racket check-food.rkt 'fried chicken'
                                            +Registering pineapple as a delicious food.
                                            +Registering sushi as a delicious food.
                                            +Registering cheesecake as a delicious food.
                                            +fried chicken is not delicious.

                                            The compile-time registrations really are happening, but Racket is automatically restricting the compile-time side-effects so they only apply at compile-time. After compilation has finished, Racket ensures that compile-time side effects are thrown away, and the run-time code starts over with fresh, untouched state. This guarantees consistent behavior, since it becomes impossible to distinguish at run-time whether a module was just compiled on the fly, or if it was pre-compiled long ago (possibly even on someone else’s computer).

                                            This is the essence of the separate compilation guarantee. To summarize:

                                            • Run-time and compile-time are distinct phases of execution, which cannot interact.

                                            • Modules can be required at multiple phases via phase shifting, but their state is kept separate. Each phase gets its own copy of the state.

                                            • Ensuring that the state is kept separate ensures predictable program behavior, no matter when the program is compiled.

                                            This summary is a simplification of phases in Racket. The full Racket module system does not have only two phases, since macros can also be used at compile-time to implement other macros, effectively creating a separate “compile-time” for the compile-time code. Each compile-time pass is isolated to its own phase, creating a finite but arbitrarily large number of distinct program phases (all but one of which occur at compile-time).

                                            Furthermore, the separate compilation guarantee does not just isolate the state of each phase from the state of other phases but also isolates all compile-time state from the compile-time state of other modules. This ensures that compilation is still deterministic even if modules are compiled in a different order, or if several modules are sometimes compiled individually while other times compiled together all at once.

                                            If you want to learn more, the full details of the module system are described at length in the General Phase Levels section of the Racket Guide, but the abridged summary I’ve described is enough for the purposes of this blog post. If the bulleted list above mostly made sense to you, you’re ready to move on.

                                            How we’re going to break it

                                            The separate compilation guarantee is a sturdy opponent, but it is not without weaknesses. Although no API in Racket, safe or unsafe, allows arbitrarily disabling phase separation, a couple features of Racket are already known to allow limited forms of cross-phase communication.

                                            The most significant of these, and the one we’ll be using as our vector of attack, is the logger. Unlike many logging systems, which are exclusively string-oriented, Racket’s logging interface allows structured logging by associating an arbitrary Racket value with each and every log message. Since it is possible to set up listeners within Racket that receive log messages sent to a particular “topic,” the logger can be used as a communication channel to send values between different parts of a program.

                                            The following program illustrates how this works. One thread creates a listener for all log messages on the topic 'send-me-a-value using make-log-receiver, then uses sync to block until a value is received. Meanwhile, a second thread sends values through the logger using log-message. Together, this creates a makeshift buffered, asynchronous channel:

                                            ;; log-comm.rkt
                                            +#lang racket
                                            +
                                            +(define t1
                                            +  (thread
                                            +   (lambda ()
                                            +     (define recv (make-log-receiver (current-logger) 'debug 'send-me-a-value))
                                            +     (let loop ()
                                            +       (println (sync recv))
                                            +       (loop)))))
                                            +
                                            +(define t2
                                            +  (thread
                                            +   (lambda ()
                                            +     (let loop ([n 0])
                                            +       (log-message (current-logger) 'debug 'send-me-a-value "" n #f)
                                            +       (sleep 1)
                                            +       (loop (add1 n))))))
                                            +
                                            +(thread-wait t1) ; wait forever
                                            $ racket log-comm.rkt
                                            +'#(debug "" 1 send-me-a-value)
                                            +'#(debug "" 2 send-me-a-value)
                                            +'#(debug "" 3 send-me-a-value)
                                            +'#(debug "" 4 send-me-a-value)
                                            +^Cuser break
                                            +

                                            In this program, the value being sent through the logger is just a number, which isn’t very interesting. But the value really can be any value, even arbitrary closures or mutable data structures. It’s even possible to send a channel through a logger, which can subsequently be used to communicate directly, without having to abuse the logger.

                                            Generally, this feature of loggers isn’t very useful, since Racket has plenty of features for cross-thread communication. What’s special about the logger, however, is that it is global, and it is cross-phase.

                                            The cross-phase nature of the logger makes some sense. If a Racket program creates a namespace (that is, a fresh environment for dynamic evaluation), then uses it to expand and compile a Racket module, the process of compilation might produce some log messages, and the calling thread might wish to receive them. It wouldn’t be a very useful logging system if log messages during compile-time were always lost. However, this convenience is a loophole in the phase separation system, since it allows values to flow—bidirectionally—between phases.

                                            This concept forms the foundation of our exploit, but it alone is not a new technique, and I did not discover it. However, all existing uses I know of that use the logger for cross-phase communication require control of the parent namespace in which modules are being compiled, which means some code must exist “outside” the actual program. That technique does not work for ordinary programs run directly with racket or compiled directly with raco make, so to get there, we’ll need something more clever.

                                            The challenge

                                            Our goal, therefore, is to share state between phases without controlling the compilation namespace. More precisely, we want to be able to create an arbitrary module-level definition that is cross-phase persistent, which means it will be evaluated once and only once no matter how many times its enclosing module is re-instantiated (i.e. given a fresh, untouched state) at various phases. A phase-shifted require of the module that contains the definition should share state with an unshifted version of the module, breaking the separate compilation guarantee wide open.

                                            To use the example from the previous section, we should be able to adjust foods.rkt very slightly…

                                            ;; foods.rkt
                                            +#lang racket
                                            +(require "define-cross-phase.rkt")
                                            +(provide delicious-food? add-delicious-food!)
                                            +
                                            +; share across phases
                                            +(define/cross-phase delicious-foods (mutable-set))
                                            +
                                            +#| ... |#

                                            …and the delicious-foods mutable state should magically become cross-phase persistent. When running check-food.rkt from source, we should see the side-effects persisted from the module’s compilation, while running from pre-compiled bytecode should give us the result with compile-time effects discarded.

                                            We already know the logger is going to be part of our exploit, but implementing define/cross-phase on top of it is more subtle than it might seem. In our previous example that used make-log-receiver, we had well-defined sender and receiver threads, but who is the “sender” in our multi-phased world? And what exactly is the sender sending?

                                            To answer those questions, allow me to outline the general idea of our approach:

                                            1. The first time our foods.rkt module is instantiated, at any phase, it evaluates the (mutable-set) expression to produce a new mutable set. It spawns a sender thread that sends this value via the logger to anyone who will listen, and that thread lingers in the background for the remaining duration of the program.

                                            2. All subsequent instantiations of foods.rkt do not evaluate the (mutable-set) expression. Instead, they obtain the existing set by creating a log receiver and obtaining the value the sender thread is broadcasting. This ensures that a single value is shared across all instantiations of the module.

                                            This sounds deceptively simple, but the crux of the problem is how to determine whether foods.rkt has previously been instantiated or not. Since we can only communicate across phases via the logger, we cannot use any shared state to directly record the first time the module is instantiated. We can listen to a log receiver and wait to see if we get a response, but this introduces a race condition: how long do we wait until giving up and deciding we’re the first instantiation? Worse, what if two threads instantiate the module at the same time, and both threads end up spawning a new sender thread, duplicating the state?

                                            The true challenge, therefore, is to develop a protocol by which we can be certain we are the first instantiation of a module, without relying on any unspecified behavior, and without introducing any race conditions. This is possible, but it isn’t obvious, and it requires combining loggers with some extra tools available to the Racket programmer.

                                            The key idea

                                            It’s finally time to tackle the key idea at the heart of our exploit: garbage collection. In Racket, garbage collection is an observable effect, since Racket allows attaching finalizers to values via wills and executors. Since a single heap is necessarily shared by the entire VM, behavior happening on other threads (even in other phases) can be indirectly observed by creating a unique value—a “canary”—then sending it to another thread, and waiting to see if it will be garbage collected or not (that is, whether or not the canary “dies”).

                                            Remember that logs and log receivers are effectively buffered, multicast, asynchronous FIFO channels. Since they are buffered, if any thread is already listening to a logger topic when a value is sent, it cannot possibly be garbage collected until that thread either reads it and discards it or the receiver itself is garbage collected. It’s possible to use this mechanism to observe whether or not another thread is already listening on a topic, as the following program demonstrates:3

                                            ;; check-receivers.rkt
                                            +#lang racket
                                            +
                                            +(define (check-receivers topic)
                                            +  (define executor (make-will-executor))
                                            +  ; limit scope of `canary` so we don’t retain a reference
                                            +  (let ()
                                            +    (define canary (gensym 'canary))
                                            +    (will-register executor canary void)
                                            +    (log-message (current-logger) 'debug topic "" canary #f))
                                            +  (if (begin
                                            +        (collect-garbage)
                                            +        (collect-garbage)
                                            +        (collect-garbage)
                                            +        (sync/timeout 0 executor))
                                            +      (printf "no receivers for ~v\n" topic)
                                            +      (printf "receiver exists for ~v\n" topic)))
                                            +
                                            +; add a receiver on topic 'foo
                                            +(define recv (make-log-receiver (current-logger) 'debug 'foo))
                                            +
                                            +(define t1 (thread (λ () (check-receivers 'foo))))
                                            +(define t2 (thread (λ () (check-receivers 'bar))))
                                            +
                                            +(thread-wait t1)
                                            +(thread-wait t2)
                                            $ racket check-receivers.rkt
                                            +no receivers for 'bar
                                            +receiver exists for 'foo
                                            +

                                            However, this program has some problems. For one, it needs to call collect-garbage several times to be certain that the canary will be collected if there are no listeners, which can take a second or two, and it also assumes that three calls to collect-garbage will be enough to collect the canary, though there is no guarantee that will be true.

                                            A bulletproof solution should be both reasonably performant and guaranteed to work. To get there, we have to combine this idea with something more. Here’s the trick: instead of sending the canary alone, send a channel alongside it. Synchronize on both the canary’s executor and the channel so that the thread will unblock if either the canary is collected or the channel is received and sent a value using channel-put. Have the receiver listen for the channel on a separate thread, and when it receives it, send a value back to unblock the waiting thread as quickly as possible, without needing to rely on a timeout or a particular number of calls to collect-garbage.

                                            Using that idea, we can revise the program:

                                            ;; check-receivers.rkt
                                            +#lang racket
                                            +
                                            +(define (check-receivers topic)
                                            +  (define chan (make-channel))
                                            +  (define executor (make-will-executor))
                                            +  ; limit scope of `canary` so we don’t retain a reference
                                            +  (let ()
                                            +    (define canary (gensym 'canary))
                                            +    (will-register executor canary void)
                                            +    (log-message (current-logger) 'debug topic ""
                                            +                 ; send the channel + the canary
                                            +                 (vector-immutable chan canary) #f))
                                            +  (if (let loop ([n 0])
                                            +        (sleep) ; yield to try to let the receiver thread work
                                            +        (match (sync/timeout 0
                                            +                             (wrap-evt chan (λ (v) 'received))
                                            +                             (wrap-evt executor (λ (v) 'collected)))
                                            +          ['collected #t]
                                            +          ['received  #f]
                                            +          [_ ; collect garbage and try again
                                            +           (collect-garbage (if (< n 3) 'minor 'major))
                                            +           (loop (add1 n))]))
                                            +      (printf "no receivers for ~v\n" topic)
                                            +      (printf "receiver exists for ~v\n" topic)))
                                            +
                                            +; add a receiver on topic 'foo
                                            +(define recv (make-log-receiver (current-logger) 'debug 'foo))
                                            +(void (thread
                                            +       (λ ()
                                            +         (let loop ()
                                            +           (match (sync recv)
                                            +             [(vector _ _ (vector chan _) _)
                                            +              (channel-put chan #t)
                                            +              (loop)])))))
                                            +
                                            +(define t1 (thread (λ () (check-receivers 'foo))))
                                            +(define t2 (thread (λ () (check-receivers 'bar))))
                                            +
                                            +(thread-wait t1)
                                            +(thread-wait t2)

                                            Now the program completes almost instantly. For this simple program, the explicit (sleep) call is effective enough at yielding that, on my machine, (check-receivers 'foo) returns without ever calling collect-garbage, and (check-receivers 'bar) returns after performing a single minor collection.

                                            This is extremely close to a bulletproof solution, but there are two remaining subtle issues:

                                            1. There is technically a race condition between the (sync recv) in the receiver thread and the subsequent channel-put, since it’s possible for the canary to be received, discarded, and garbage collected before reaching the call to channel-put, which the sending thread would incorrectly interpret as indicating the topic has no receivers.

                                              To fix that, the receiver thread can send the canary itself back through the channel, which fundamentally has to work, since the value cannot be collected until it has been received by the sending thread, at which point the sync has already chosen the channel.

                                            2. It is possible for the receiver thread to receive the log message and call channel-put, but for the sending thread to somehow die in the meantime (which cannot be protected against in general in Racket, since thread-kill immediately and forcibly terminates a thread). If this were to happen, the sending thread would never obtain the value from the channel, blocking the receiving thread indefinitely.

                                              A solution is to spawn a new thread for each channel-put instead of calling it directly from the receiving thread. Conveniently, this both ensures the receiving thread never gets stuck and avoids resource leaks, since the Racket runtime is smart enough to GC a thread blocked on a channel that has no other references and therefore can never be unblocked.

                                            With those fixes in place, the program is, to the best of my knowledge, bulletproof. It will always correctly determine whether or not a logger has a listener, with no race conditions or reliance upon unspecified behavior of the Racket runtime. It does, however, make a couple of assumptions.

                                            First, it assumes that the value of (current-logger) is shared between the threads. It is true that (current-logger) can be changed, and sometimes is, but it’s usually done via parameterize, not mutation of the parameter directly. Therefore, this can largely be mitigated by storing the value of (current-logger) at module instantiation time.

                                            Second, it assumes that no other receivers are listening on the same topic. Technically, even using a unique, uninterned key for the topic is insufficient to ensure that no receivers are listening to it, since a receiver can choose to listen to all topics. However, in practice, it is highly unlikely that any receiver would willfully choose to listen to all topics at the 'debug level, since the receiver would be inundated with enormous amounts of useless information. Even if such a receiver were to be created, it is highly likely that it would dequeue the messages as quickly as possible and discard the accompanying payload, since doing otherwise would cause all messages to be retained in memory, leading to a significant memory leak.

                                            Both these problems can be mitigated by using a logger other than the root logger, which is easy in this example. However, for the purpose of subverting the separate compilation guarantee, we would have no way to share the logger object itself across phases, defeating the whole purpose, so we are forced to use the root logger and hope the above two assumptions remain true (but they usually do).

                                            Preparing the exploit

                                            If you’ve made it here, congratulations! The most difficult part of this blog post is over. All that’s left is the fun part: performing the exploit.

                                            The bulk of our implementation is a slightly adapted version of check-receivers:

                                            ;; define-cross-phase.rkt
                                            +#lang racket
                                            +
                                            +(define root-logger (current-logger))
                                            +
                                            +(define (make-cross-phase topic thunk)
                                            +  (define receiver (make-log-receiver root-logger 'debug topic))
                                            +  (define chan (make-channel))
                                            +  (define executor (make-will-executor))
                                            +
                                            +  (let ()
                                            +    (define canary (gensym 'canary))
                                            +    (will-register executor canary (λ (v) 'collected))
                                            +    (log-message root-logger 'debug topic ""
                                            +                 (vector-immutable canary chan) #f)
                                            +    (let loop ()
                                            +      (match (sync receiver)
                                            +        [(vector _ _ (vector _ (== chan eq?)) _)
                                            +         (void)]
                                            +        [_
                                            +         (loop)])))
                                            +
                                            +  (define execute-evt (wrap-evt executor will-execute))
                                            +  (define result (let loop ([n 0])
                                            +                   (sleep)
                                            +                   (or (sync/timeout 0 chan execute-evt)
                                            +                       (begin
                                            +                         (collect-garbage (if (< n 3) 'minor 'major))
                                            +                         (loop (add1 n))))))
                                            +
                                            +  (match result
                                            +    [(vector _ value)
                                            +     value]
                                            +    ['collected
                                            +     (define value (thunk))
                                            +     (thread
                                            +      (λ ()
                                            +        (let loop ()
                                            +          (match (sync receiver)
                                            +            [(vector _ _ (vector canary chan) _)
                                            +             (thread (λ () (channel-put chan (vector-immutable canary value))))
                                            +             (loop)]))))
                                            +     value]))

                                            There are a few minor differences, which I’ll list:

                                            1. The most obvious difference is that make-cross-phase does the work of both checking if a receiver exists—which I’ll call the manager thread—and spawning it if it doesn’t. If it does end up spawning a manager thread, it evaluates the given thunk to produce a value, which becomes the cross-phase value that will be sent through the channel alongside the canary.

                                            2. Once the manager thread is created, subsequent calls to make-cross-phase will receive the value through the channel and return it instead of re-invoking thunk. This is what ensures the right-hand side of each use of define/cross-phase is only ever evaluated once.

                                            3. Since make-cross-phase needs to create a log receiver if no manager thread exists, it does so immediately, before sending the canary through the logger. This avoids a race condition between multiple threads that are simultaneously competing to become the manager thread, where both threads could send a canary through the logger before either was listening, both canaries would get GC’d, and both threads would spawn a new manager.4

                                              Creating the receiver before sending the canary avoids this problem, but the thread now needs to receive its own canary and discard it before synchronizing on the channel and executor, since otherwise it will retain a reference to the canary. It’s possible that in between creating the receiver and sending the canary, another thread also sent a canary, so it needs to drop any log messages it finds that don’t include its own canary.

                                              This ends up working out perfectly, since every thread drops all the messages received before the one containing its own canary, but retains all subsequent values. This means that only one thread can ever “win” and become the manager, since the first thread to send a canary is guaranteed to retain all subsequent canaries, yet also guaranteed its canary will be GC’d. Other threads racing to become the manager will remain blocked until the manager thread is created, since its canaries will be retained by the manager-to-be until it dequeues them.

                                              (This is the most subtle part of the process to get right, but conveniently, it mostly just works out without very much code. If you didn’t understand any of the above three paragraphs, it isn’t a big deal.)

                                            The final piece to this puzzle is to define the define/cross-phase macro that wraps make-cross-phase. The macro is actually slightly more involved than just generating a call to make-cross-phase directly, since we’d like to use an uninterned symbol for the topic instead of an interned one, just to ensure it is unique. Ordinarily, this might seem impossible, since an uninterned symbol is fundamentally a unique value that needs to be communicated across phases, and the whole problem we are solving is creating a communication channel that spans phases. However, Racket actually provides some built-in support for sharing uninterned symbols across phases (plus some other kinds of values, but they must always be immutable). To do this, we need to generate a cross-phase persistent submodule that exports an uninterned symbol, then pass that symbol as the topic to make-cross-phase:

                                            (require (for-syntax racket/syntax)
                                            +         syntax/parse/define)
                                            +
                                            +(provide define/cross-phase)
                                            +
                                            +(define-simple-macro (define/cross-phase x:id e:expr)
                                            +  #:with topic-mod-name (generate-temporary 'cross-phase-topic-key)
                                            +  (begin
                                            +    (module topic-mod-name '#%kernel
                                            +      (#%declare #:cross-phase-persistent)
                                            +      (#%provide topic)
                                            +      (define-values [topic] (gensym "cross-phase")))
                                            +    (require 'topic-mod-name)
                                            +    (define x (make-cross-phase topic (λ () e)))))

                                            And that’s really it. We’re done.

                                            Executing the exploit

                                            With our implementation of define/cross-phase in hand, all that’s left to do is run our original check-foods.rkt program and see what happens:

                                            $ racket check-food.rkt 'fried chicken'
                                            +set-add!: contract violation:
                                            +expected: set?
                                            +given: (mutable-set "fried chicken" "roasted chicken" "roasted potato" "fried potato")
                                            +argument position: 1st
                                            +other arguments...:
                                            +  x: "pineapple"

                                            Well, I don’t know what you expected. Play stupid games, win stupid prizes.

                                            This error actually makes sense, but it belies one reason (of many) why this whole endeavor is probably a bad idea. Although we’ve managed to make our mutable set cross-phase persistent, our references to set operations like set-add! and set-member? are not, and every time racket/set is instantiated in a fresh phase, it creates an entirely new instance of the set structure type. This means that even though we have a bona fide mutable set, it isn’t actually the type of set that this phase’s set-add! understands!

                                            Of course, this isn’t a problem that some liberal application of define/cross-phase can’t solve:

                                            ;; foods.rkt
                                            +#lang racket
                                            +(require "define-cross-phase.rkt")
                                            +(provide delicious-food? add-delicious-food!)
                                            +
                                            +(define/cross-phase cross:set-member? set-member?)
                                            +(define/cross-phase cross:set-add! set-add!)
                                            +
                                            +(define/cross-phase delicious-foods (mutable-set))
                                            +
                                            +(define (delicious-food? food)
                                            +  (cross:set-member? delicious-foods food))
                                            +
                                            +(define (add-delicious-food! new-food)
                                            +  (cross:set-add! delicious-foods new-food))
                                            $ racket check-food.rkt 'fried chicken'
                                            +fried chicken is a delicious food.
                                            +$ raco make check-food.rkt
                                            +$ racket check-food.rkt 'fried chicken'
                                            +fried chicken is not delicious.

                                            And thus we find that another so-called “guarantee” isn’t.

                                            Reflection

                                            Now comes the time in the blog post when I have to step back and think about what I’ve done. Have mercy.

                                            Everything in this blog post is a terrible idea. No, you should not use loggers for anything that isn’t logging, you shouldn’t use wills and executors for critical control flow, and obviously you should absolutely not intentionally break one of the most helpful guarantees the Racket module system affords you.

                                            But I thought it was fun to do all that, anyway.

                                            The meaningful takeaways from this blog post aren’t that the separate compilation guarantee can be broken, nor that any of the particular techniques I used hold, but that

                                            1. ensuring non-trivial guarantees is really hard,

                                            2. despite that, the separate compilation guarantee is really, really hard to break,

                                            3. the separate compilation guarantee is good, and you should appreciate the luxury it affords you while writing Racket macros,

                                            4. avoiding races in a concurrent environment can be extremely subtle,

                                            5. and Racket is totally awesome for giving me this much rope to hang myself with.

                                            If you want to hang yourself with Racket, too, runnable code from this blog post is available here.

                                            1. This isn’t strictly true, since Racket provides sandboxing mechanisms that can compile and execute untrusted code without file system or network access, but this is not the default compilation mode. Usually, it doesn’t matter nearly as much as it might sound: most of the time, if you’re compiling untrusted code, you’re also going to run it, and running untrusted code can do all those things, anyway.

                                            2. This is actually a terrible use case for a macro, since an ordinary function would do just fine, but I’m simplifying a little to keep the example small.

                                            3. Racket actually provides this functionality directly via the log-level? procedure. However, since log-level? provides no way to determine how many receivers are listening to a topic, using it to guard against creating a receiver is vulnerable to a race condition that the garbage collection-based approach can avoid, as is discussed later. Furthermore, the GC technique is more likely to be resilient to nosy log receivers listening on all topics at the 'debug level, since they will almost certainly dequeue and discard the value quickly (as otherwise they would leak large quantities of memory).

                                            4. This race is the one that makes using log-level? untenable, since the receiver needs to be created before the topic is checked for listeners to avoid the race, which can’t be done with log-level? (since it would always return #t).

                                            \ No newline at end of file diff --git a/blog/2019/09/07/demystifying-monadbasecontrol/index.html b/blog/2019/09/07/demystifying-monadbasecontrol/index.html new file mode 100644 index 0000000..dd546c8 --- /dev/null +++ b/blog/2019/09/07/demystifying-monadbasecontrol/index.html @@ -0,0 +1,265 @@ +Demystifying MonadBaseControl

                                            Demystifying MonadBaseControl

                                            ⦿ haskell

                                            MonadBaseControl from the monad-control package is a confusing typeclass, and its methods have complicated types. For many people, it’s nothing more than scary, impossible-to-understand magic that is, for some reason, needed when lifting certain kinds of operations. Few resources exist that adequately explain how, why, and when it works, which sadly seems to have resulted in some FUD about its use.

                                            There’s no doubt that the machinery of MonadBaseControl is complex, and the role it plays in practice is often subtle. However, its essence is actually much simpler than it appears, and I promise it can be understood by mere mortals. In this blog post, I hope to provide a complete survey of MonadBaseControl—how it works, how it’s designed, and how it can go wrong—in a way that is accessible to anyone with a firm grasp of monads and monad transformers. To start, we’ll motivate MonadBaseControl by reinventing it ourselves.

                                            The higher-order action problem

                                            Say we have a function with the following type:1

                                            foo :: IO a -> IO a

                                            If we have an action built from a transformer stack like

                                            bar :: StateT X IO Y

                                            then we might wish to apply foo to bar, but that is ill-typed, since IO is not the same as StateT X IO. In cases like these, we often use lift, but it’s not good enough here: lift adds a new monad transformer to an action, but here we need to remove a transformer. So we need a function with a type like this:

                                            unliftState :: StateT X IO Y -> IO Y

                                            However, if you think about that type just a little bit, it’s clear something’s wrong: it throws away information, namely the state. You may remember that a StateT X IO Y action is equivalent to a function of type X -> IO (Y, X), so our hypothetical unliftState function has two problems:

                                            1. We have no X to use as the initial state.

                                            2. We’ll lose any modifications bar made to the state, since the result type is just Y, not (Y, X).

                                            Clearly, we’ll need something more sophisticated, but what?

                                            A naïve solution

                                            Given that foo doesn’t know anything about the state, we can’t easily thread it through foo itself. However, by using runStateT explicitly, we could do some of the state management ourselves:

                                            foo' :: StateT s IO a -> StateT s IO a
                                            +foo' m = do
                                            +  s <- get
                                            +  (v, s') <- lift $ foo (runStateT m s)
                                            +  put s'
                                            +  pure v

                                            Do you see what’s going on there? It’s not actually very complicated: we get the current state, then pass it as the initial state to runStateT. This produces an action IO (a, s) that has closed over the current state. We can pass that action to foo without issue, since foo is polymorphic in the action’s return type. Finally, all we have to do is put the modified state back into the enclosing StateT computation, and we can get on with our business.

                                            That strategy works okay when we only have one monad transformer, but it gets hairy quickly as soon as we have two or more. For example, if we had baz :: ExceptT X (StateT Y IO) Z, then we could do the same trick by getting the underlying

                                            Y -> IO (Either X Z, Y)

                                            function, closing over the state, restoring it, and doing the appropriate case analysis to re-raise any ExceptT errors, but that’s a lot of work to do for every single function! What we’d like to do instead is somehow abstract over the pattern we used to write foo' in a way that scales to arbitrary monad transformers.

                                            The essence of MonadBaseControl

                                            To build a more general solution for “unlifting” arbitrary monad transformers, we need to start thinking about monad transformer state. The technique we used to implement foo' operated on the following process:

                                            1. Capture the action’s input state and close over it.

                                            2. Package up the action’s output state with its result and run it.

                                            3. Restore the action’s output state into the enclosing transformer.

                                            4. Return the action’s result.

                                            For StateT s, it turns out that the input state and output state are both s, but other monad transformers have state, too. Consider the input and output state for the following common monad transformers:

                                            + + + + + + + + + + + + + + + + + + + + + + + + + +
                                            transformerrepresentationinput stateoutput state
                                            StateT s m as -> m (a, s)ss
                                            ReaderT r m ar -> m ar()
                                            WriterT w m am (a, w)()w
                                            +

                                            Notice how the input state is whatever is to the left of the ->, while the output state is whatever extra information gets produced alongside the result. Using the same reasoning, we can also deduce the input and output state for compositions of multiple monad transformers, such as the following:

                                            + + + + + + + + + + + + + + + + + + + + + + + + + +
                                            transformerrepresentationinput stateoutput state
                                            ReaderT r (WriterT w m) ar -> m (a, w)rw
                                            StateT s (ReaderT r m) ar -> s -> m (a, s)(r, s)s
                                            WriterT w (StateT s m) as -> m ((a, w), s)s(w, s)
                                            +

                                            Notice that when monad transformers are composed, their states are composed, too. This is useful to keep in mind, since our goal is to capture the four steps above in a typeclass, polymorphic in the state of the monad transformers we need to lift through. At minimum, we need two new operations: one to capture the input state and close over it (step 1) and one to restore the output state (step 3). One class we might come up with could look like this:

                                            class MonadBase b m => MonadBaseControl b m | m -> b where
                                            +  type InputState m
                                            +  type OutputState m
                                            +  captureInputState :: m (InputState m)
                                            +  closeOverInputState :: m a -> InputState m -> b (a, OutputState m)
                                            +  restoreOutputState :: OutputState m -> m ()

                                            If we can write instances of that typeclass for various transformers, we can use the class’s operations to implement foo' in a generic way that works with any combination of them:

                                            foo' :: MonadBaseControl IO m => m a -> m a
                                            +foo' m = do
                                            +  s <- captureInputState
                                            +  let m' = closeOverInputState m s
                                            +  (v, s') <- liftBase $ foo m'
                                            +  restoreOutputState s'
                                            +  pure v

                                            So how do we implement those instances? Let’s start with IO, since that’s the base case:

                                            instance MonadBaseControl IO IO where
                                            +  type InputState IO = ()
                                            +  type OutputState IO = ()
                                            +  captureInputState = pure ()
                                            +  closeOverInputState m () = m <&> (, ())
                                            +  restoreOutputState () = pure ()

                                            Not very exciting. The StateT s instance, on the other hand, is significantly more interesting:

                                            instance MonadBaseControl b m => MonadBaseControl b (StateT s m) where
                                            +  type InputState (StateT s m) = (s, InputState m)
                                            +  type OutputState (StateT s m) = (s, OutputState m)
                                            +  captureInputState = (,) <$> get <*> lift captureInputState
                                            +  closeOverInputState m (s, ss) = do
                                            +    ((v, s'), ss') <- closeOverInputState (runStateT m s) ss
                                            +    pure (v, (s', ss'))
                                            +  restoreOutputState (s, ss) = lift (restoreOutputState ss) *> put s

                                            This instance alone includes most of the key ideas behind MonadBaseControl. There’s a lot going on, so let’s break it down, step by step:

                                            1. Start by examining the definitions of InputState and OutputState. Are they what you expected? You’d be forgiven for expecting the following:

                                              type InputState (StateT s m) = s
                                              +type OutputState (StateT s m) = s

                                              After all, that’s what we wrote in the table, isn’t it?

                                              However, if you give it a try, you’ll find it doesn’t work. InputState and OutputState must capture the state of the entire monad, not just a single transformer layer, so we have to combine the StateT s state with the state of the underlying monad. In the simplest case we get

                                              InputState (StateT s IO) = (s, ())

                                              which is boring, but in a more complex case, we need to get something like this:

                                              InputState (StateT s (ReaderT IO)) = (s, (r, ()))

                                              Therefore, InputState (StateT s m) combines s with InputState m in a tuple, and OutputState does the same.

                                            2. Moving on, take a look at captureInputState and closeOverInputState. Just as InputState and OutputState capture the state of the entire monad, these functions need to be inductive in the same way.

                                              captureInputState acquires the current state using get, and it combines it with the remaining monadic state using lift captureInputState. closeOverInputState uses the captured state to peel off the outermost StateT layer, then calls closeOverInputState recursively to peel off the rest of them.

                                            3. Finally, restoreOutputState restores the state of the underlying monad stack, then restores the StateT state, ensuring everything ends up back the way it’s supposed to be.

                                            Take the time to digest all that—work through it yourself if you need to—as it’s a dense piece of code. Once you feel comfortable with it, take a look at the instances for ReaderT and WriterT as well:

                                            instance MonadBaseControl b m => MonadBaseControl b (ReaderT r m) where
                                            +  type InputState (ReaderT r m) = (r, InputState m)
                                            +  type OutputState (ReaderT r m) = OutputState m
                                            +  captureInputState = (,) <$> ask <*> lift captureInputState
                                            +  closeOverInputState m (s, ss) = closeOverInputState (runReaderT m s) ss
                                            +  restoreOutputState ss = lift (restoreOutputState ss)
                                            +
                                            +instance (MonadBaseControl b m, Monoid w) => MonadBaseControl b (WriterT w m) where
                                            +  type InputState (WriterT w m) = InputState m
                                            +  type OutputState (WriterT w m) = (w, OutputState m)
                                            +  captureInputState = lift captureInputState
                                            +  closeOverInputState m ss = do
                                            +    ((v, s'), ss') <- closeOverInputState (runWriterT m) ss
                                            +    pure (v, (s', ss'))
                                            +  restoreOutputState (s, ss) = lift (restoreOutputState ss) *> tell s

                                            Make sure you understand these instances, too. It should be easier this time, since they share most of their structure with the StateT instance, but note the asymmetry that arises from the differing input and output states. (It may even help to try and write these instances yourself, focusing on the types whenever you get stuck.)

                                            If you feel alright with them, then congratulations: you’re already well on your way to grokking MonadBaseControl!

                                            Hiding the input state

                                            So far, our implementation of MonadBaseControl works, but it’s actually slightly more complicated than it needs to be. As it happens, all valid uses of MonadBaseControl will always end up performing the following pattern:

                                            s <- captureInputState
                                            +let m' = closeOverInputState m s

                                            That is, we close over the input state as soon as we capture it. We can therefore combine captureInputState and closeOverInputState into a single function:

                                            captureAndCloseOverInputState :: m a -> m (b (a, OutputState m))

                                            What’s more, we no longer need the InputState associated type at all! This is an improvement, since it simplifies the API and removes the possibility for any misuse of the input state, since it’s never directly exposed. On the other hand, it has a more complicated type: it produces a monadic action that returns another monadic action. This can be a little more difficult to grok, which is why I presented the original version first, but it may help to consider how the above type arises naturally from the following definition:

                                            captureAndCloseOverInputState m = closeOverInputState m <$> captureInputState

                                            Let’s update the MonadBaseControl class to incorporate this simplification:

                                            class MonadBase b m => MonadBaseControl b m | m -> b where
                                            +  type OutputState m
                                            +  captureAndCloseOverInputState :: m a -> m (b (a, OutputState m))
                                            +  restoreOutputState :: OutputState m -> m ()

                                            We can then update all the instances to use the simpler API by simply fusing the definitions of captureInputState and closeOverInputState together:

                                            instance MonadBaseControl IO IO where
                                            +  type OutputState IO = ()
                                            +  captureAndCloseOverInputState m = pure (m <&> (, ()))
                                            +  restoreOutputState () = pure ()
                                            +
                                            +instance MonadBaseControl b m => MonadBaseControl b (StateT s m) where
                                            +  type OutputState (StateT s m) = (s, OutputState m)
                                            +  captureAndCloseOverInputState m = do
                                            +    s <- get
                                            +    m' <- lift $ captureAndCloseOverInputState (runStateT m s)
                                            +    pure $ do
                                            +      ((v, s'), ss') <- m'
                                            +      pure (v, (s', ss'))
                                            +  restoreOutputState (s, ss) = lift (restoreOutputState ss) *> put s
                                            +
                                            +instance MonadBaseControl b m => MonadBaseControl b (ReaderT r m) where
                                            +  type OutputState (ReaderT r m) = OutputState m
                                            +  captureAndCloseOverInputState m = do
                                            +    s <- ask
                                            +    lift $ captureAndCloseOverInputState (runReaderT m s)
                                            +  restoreOutputState ss = lift (restoreOutputState ss)
                                            +
                                            +instance (MonadBaseControl b m, Monoid w) => MonadBaseControl b (WriterT w m) where
                                            +  type OutputState (WriterT w m) = (w, OutputState m)
                                            +  captureAndCloseOverInputState m = do
                                            +    m' <- lift $ captureAndCloseOverInputState (runWriterT m)
                                            +    pure $ do
                                            +      ((v, s'), ss') <- m'
                                            +      pure (v, (s', ss'))
                                            +  restoreOutputState (s, ss) = lift (restoreOutputState ss) *> tell s

                                            This is already very close to a full MonadBaseControl implementation. The captureAndCloseOverInputState implementations are getting a little out of hand, but bear with me—they’ll get simpler before this blog post is over.

                                            Coping with partiality

                                            Our MonadBaseControl class now works with StateT, ReaderT, and WriterT, but one transformer we haven’t considered is ExceptT. Let’s try to extend our table from before with a row for ExceptT:

                                            + + + + + + + + + + + + + +
                                            transformerrepresentationinput stateoutput state
                                            ExceptT e m am (Either e a)()???
                                            +

                                            Hmm… what is the output state for ExceptT?

                                            The answer can’t be e, since we might not end up with an e—the computation might not fail. Maybe e would be closer… could that work?

                                            Well, let’s try it. Let’s write a MonadBaseControl instance for ExceptT:

                                            instance MonadBaseControl b m => MonadBaseControl b (ExceptT e m) where
                                            +  type OutputState (ExceptT e m) = (Maybe e, OutputState m)
                                            +  captureAndCloseOverInputState m = do
                                            +    m' <- lift $ captureAndCloseOverInputState (runExceptT m)
                                            +    pure $ do
                                            +      ((v, s'), ss') <- m'
                                            +      pure (v, (s', ss'))
                                            +  restoreOutputState (s, ss) = lift (restoreOutputState ss) *> case s of
                                            +    Just e -> throwError e
                                            +    Nothing -> pure ()

                                            Sadly, the above implementation doesn’t typecheck; it is rejected with the following type error:

                                            • Couldn't match type ‘Either e a’ with ‘(a, Maybe e)’
                                            +  Expected type: m (b ((a, Maybe e), OutputState m))
                                            +    Actual type: m (b (Either e a, OutputState m))
                                            +• In the second argument of ‘($)’, namely
                                            +    ‘captureAndCloseOverInputState (runExceptT m)’
                                            +  In a stmt of a 'do' block:
                                            +    m' <- lift $ captureAndCloseOverInputState (runExceptT m)
                                            +  In the expression:
                                            +    do m' <- lift $ captureAndCloseOverInputState (runExceptT m)
                                            +       return do ((v, s'), ss') <- m'
                                            +                 pure (v, (s', ss'))
                                            +

                                            We promised a (a, Maybe e), but we have an Either e a, and there’s certainly no way to get the former from the latter. Are we stuck? (If you’d like, take a moment to think about how you’d solve this type error before moving on, as it may be helpful for understanding the following solution.)

                                            The fundamental problem here is partiality. The type of the captureAndCloseOverInputState method always produces an action in the base monad that includes an a in addition to some other output state. But ExceptT is different: when it an error is raised, it doesn’t produce an a at all—it only produces an e. Therefore, as written, it’s impossible to give ExceptT a MonadBaseControl instance.

                                            Of course, we’d very much like to give ExceptT a MonadBaseControl instance, so that isn’t very satisfying. Somehow, we need to change captureAndCloseOverInputState so that it doesn’t always need to produce an a. There are a few ways we could accomplish that, but an elegant way to do it is this:

                                            class MonadBase b m => MonadBaseControl b m | m -> b where
                                            +  type WithOutputState m a
                                            +  captureAndCloseOverInputState :: m a -> m (b (WithOutputState m a))
                                            +  restoreOutputState :: WithOutputState m a -> m a

                                            We’ve replaced the old OutputState associated type with a new WithOutputState type, and the key difference between them is that WithOutputState describes the type of a combination of the result (of type a) and the output state, rather than describing the type of the output state alone. For total monad transformers like StateT, ReaderT, and WriterT, WithOutputState m a will just be a tuple of the result value and the output state, the same as before. For example, here’s an updated MonadBaseControl instance for StateT:

                                            instance MonadBaseControl b m => MonadBaseControl b (StateT s m) where
                                            +  type WithOutputState (StateT s m) a = WithOutputState m (a, s)
                                            +  captureAndCloseOverInputState m = do
                                            +    s <- get
                                            +    lift $ captureAndCloseOverInputState (runStateT m s)
                                            +  restoreOutputState ss = do
                                            +    (a, s) <- lift $ restoreOutputState ss
                                            +    put s
                                            +    pure a

                                            Before we consider how this helps us with ExceptT, let’s pause for a moment and examine the revised StateT instance in detail, as there are some new things going on here:

                                            • Take a close look at the definition of WithOutputState (StateT s m) a. Note that we’ve defined it to be WithOutputState m (a, s), not (WithOutputState m a, s). Consider, for a moment, the difference between these types. Can you see why we used the former, not the latter?

                                              If it’s unclear to you, that’s okay—let’s illustrate the difference with an example. Consider two similar monad transformer stacks:

                                              m1 :: StateT s (ExceptT e IO) a
                                              +m2 :: ExceptT e (StateT s IO) a

                                              Both these stacks contain StateT and ExceptT, but they are layered in a different order. What’s the difference? Well, consider what m1 and m2 return once fully unwrapped:

                                              runExceptT (runStateT m1 s) :: m (Either e (a, s))
                                              +runStateT (runExceptT m2) s :: m (Either e a, s)

                                              These results are meaningfully different: in m1, the state is discarded if an error is raised, but in m2, the final state is always returned, even if the computation is aborted. What does this mean for WithOutputState?

                                              Here’s the important detail: the state is discarded when ExceptT is “inside” StateT, not the other way around. This can be counterintuitive, since the s ends up inside the Either when the StateT constructor is on the outside and vice versa. This is really just a property of how monad transformers compose, not anything specific to MonadBaseControl, so an explanation of why this happens is outside the scope of this blog post, but the relevant insight is that the m in StateT s m a controls the eventual action’s output state.

                                              If we had defined WithOutputState (StateT s m) a to be (WithOutputState m a, s), we’d be in a pickle, since m would be unable to influence the presence of s in the output state. Therefore, we have no choice but to use WithOutputState m (a, s). (If you are still confused by this, try it yourself; you’ll find that there’s no way to make the other definition typecheck.)

                                            • Now that we’ve developed an intuitive understanding of why WithOutputState must be defined the way it is, let’s look at things from another perspective. Consider the type of runStateT once more:

                                              runStateT :: StateT s m a -> s -> m (a, s)

                                              Note that the result type is m (a, s), with the m on the outside. As it happens, this correspondence simplifies the definition of captureAndCloseOverInputState, since we no longer have to do any fiddling with its result—it’s already in the proper shape, so we can just return it directly.

                                            • Finally, this instance illustrates an interesting change to restoreOutputState. Since the a is now packed inside the WithOutputState m a value, the caller of captureAndCloseOverInputState needs some way to get the a back out! Conveniently, restoreOutputState can play that role, both restoring the output state and unpacking the result.

                                              Even ignoring partial transformers like ExceptT, this is an improvement over the old API, as it conveniently prevents the programmer from forgetting to call restoreOutputState. However, as we’ll see shortly, it is much more than a convenience: once ExceptT comes into play, it is essential!

                                            With those details addressed, let’s return to ExceptT. Using the new interface, writing an instance for ExceptT is not only possible, it’s actually rather easy:

                                            instance MonadBaseControl b m => MonadBaseControl b (ExceptT e m) where
                                            +  type WithOutputState (ExceptT e m) a = WithOutputState m (Either e a)
                                            +  captureAndCloseOverInputState m =
                                            +    lift $ captureAndCloseOverInputState (runExceptT m)
                                            +  restoreOutputState ss =
                                            +    either throwError pure =<< lift (restoreOutputState ss)

                                            This instance illustrates why it’s so crucial that restoreOutputState have the aforementioned dual role: it must handle the case where no a exists at all! In the case of ExceptT, it restores the state in the enclosing monad by re-raising an error.

                                            Now all that’s left to do is update the other instances:

                                            instance MonadBaseControl IO IO where
                                            +  type WithOutputState IO a = a
                                            +  captureAndCloseOverInputState = pure
                                            +  restoreOutputState = pure
                                            +
                                            +instance MonadBaseControl b m => MonadBaseControl b (ReaderT r m) where
                                            +  type WithOutputState (ReaderT r m) a = WithOutputState m a
                                            +  captureAndCloseOverInputState m = do
                                            +    s <- ask
                                            +    lift $ captureAndCloseOverInputState (runReaderT m s)
                                            +  restoreOutputState ss = lift $ restoreOutputState ss
                                            +
                                            +instance (MonadBaseControl b m, Monoid w) => MonadBaseControl b (WriterT w m) where
                                            +  type WithOutputState (WriterT w m) a = WithOutputState m (a, w)
                                            +  captureAndCloseOverInputState m =
                                            +    lift $ captureAndCloseOverInputState (runWriterT m)
                                            +  restoreOutputState ss = do
                                            +    (a, s) <- lift $ restoreOutputState ss
                                            +    tell s
                                            +    pure a

                                            Finally, we can update our lifted variant of foo to use the new interface so it will work with transformer stacks that include ExceptT:

                                            foo' :: MonadBaseControl IO m => m a -> m a
                                            +foo' m = do
                                            +  m' <- captureAndCloseOverInputState m
                                            +  restoreOutputState =<< liftBase (foo m')

                                            At this point, it’s worth considering something: although getting the MonadBaseControl class and instances right was a lot of work, the resulting foo' implementation is actually incredibly simple. That’s a good sign, since we only have to write the MonadBaseControl instances once (in a library), but we have to write functions like foo' quite often.

                                            Scaling to the real MonadBaseControl

                                            The MonadBaseControl class we implemented in the previous section is complete. It is a working, useful class that is equivalent in power to the “real” MonadBaseControl class in the monad-control library. However, if you compare the two, you’ll notice that the version in monad-control looks a little bit different. What gives?

                                            Let’s compare the two classes side by side:

                                            -- ours
                                            +class MonadBase b m => MonadBaseControl b m | m -> b where
                                            +  type WithOutputState m a
                                            +  captureAndCloseOverInputState :: m a -> m (b (WithOutputState m a))
                                            +  restoreOutputState :: WithOutputState m a -> m a
                                            +
                                            +-- theirs
                                            +class MonadBase b m => MonadBaseControl b m | m -> b where
                                            +  type StM m a
                                            +  liftBaseWith :: (RunInBase m b -> b a) -> m a
                                            +  restoreM :: StM m a -> m a

                                            Let’s start with the similarities, since those are easy:

                                            • Our WithOutputState associated type is precisely equivalent to their StM associated type, they just use a (considerably) shorter name.

                                            • Likewise, our restoreOutputState method is precisely equivalent to their restoreM method, simply under a different name.

                                            That leaves captureAndCloseOverInputState and liftBaseWith. Those two methods both do similar things, but they aren’t identical, and that’s where all the differences lie. To understand liftBaseWith, let’s start by inlining the definition of the RunInBase type alias so we can see the fully-expanded type:

                                            liftBaseWith
                                            +  :: MonadBaseControl b m
                                            +  => ((forall c. m c -> b (StM m c)) -> b a)
                                            +  -> m a

                                            That type is complicated! However, if we break it down, hopefully you’ll find it’s not as scary as it first appears. Let’s reimplement the foo' example from before using liftBaseWith to show how this version of MonadBaseControl works:

                                            foo' :: MonadBaseControl IO m => m a -> m a
                                            +foo' m = do
                                            +  s <- liftBaseWith $ \runInBase -> foo (runInBase m)
                                            +  restoreM s

                                            This is, in some ways, superficially similar to the version we wrote using our version of MonadBaseControl. Just like in our version, we capture the input state, apply foo in the IO monad, then restore the state. But what exactly is doing the state capturing, and what is runInBase?

                                            Let’s start by adding a type annotation to runInBase to help make it a little clearer what’s going on:

                                            foo' :: forall m a. MonadBaseControl IO m => m a -> m a
                                            +foo' m = do
                                            +  s <- liftBaseWith $ \(runInBase :: forall b. m b -> IO (StM m b)) ->
                                            +    foo (runInBase m)
                                            +  restoreM s

                                            That type should look sort of recognizable. If we replace StM with WithOutputState, then we get a type that looks very similar to that of our original closeOverInputState function, except it doesn’t need to take the input state as an argument. How does that work?

                                            Here’s the trick: liftBaseWith starts by capturing the input state, just as before. However, it then builds a function, runInBase, which is like closeOverInputState partially-applied to the input state it captured. It hands that function to us, and we’re free to apply it to m, which produces the IO (StM m a) action we need, and we can now pass that action to foo. The result is returned in the outer monad, and we restore the state using restoreM.

                                            Sharing the input state

                                            At first, this might seem needlessly complicated. When we first started, we separated capturing the input state and closing over it into two separate operations (captureInputState and closeOverInputState), but we eventually combined them so that we could keep the input state hidden. Why does monad-control split them back into two operations again?

                                            As it turns out, when lifting foo, there’s no advantage to the more complicated API of monad-control. In fact, we could implement our captureAndCloseOverInputState operation in terms of liftBaseWith, and we could use that to implement foo' the same way we did before:

                                            captureAndCloseOverInputState :: MonadBaseControl b m => m a -> m (b (StM m a))
                                            +captureAndCloseOverInputState m = liftBaseWith $ \runInBase -> pure (runInBase m)
                                            +
                                            +foo' :: MonadBaseControl IO m => m a -> m a
                                            +foo' m = do
                                            +  m' <- captureAndCloseOverInputState m
                                            +  restoreM =<< liftBase (foo m')

                                            However, that approach has a downside once we need to lift more complicated functions. foo is exceptionally simple, as it only accepts a single input argument, but what if we wanted to lift a more complicated function that took two monadic arguments, such as this one:

                                            bar :: IO a -> IO a -> IO a

                                            We could implement that by calling captureAndCloseOverInputState twice, like this:

                                            bar' :: MonadBaseControl IO m => m a -> m a -> m a
                                            +bar' ma mb = do
                                            +  ma' <- captureAndCloseOverInputState ma
                                            +  mb' <- captureAndCloseOverInputState mb
                                            +  restoreM =<< liftBase (bar ma' mb')

                                            However, that would capture the monadic state twice, which is rather inefficient. By using liftBaseWith, the state capturing is done just once, and it’s shared between all calls to runInBase:

                                            bar' :: MonadBaseControl IO m => m a -> m a -> m a
                                            +bar' ma mb = do
                                            +  s <- liftBaseWith $ \runInBase ->
                                            +    bar (runInBase ma) (runInBase mb)
                                            +  restoreM s

                                            By providing a “running” function (runInBase) instead of direct access to the input state, liftBaseWith allows sharing the captured input state between multiple actions without exposing it directly.

                                            Sidebar: continuation-passing and impredicativity

                                            One last point before we move on: although the above explains why captureAndCloseOverInputState is insufficient, you may be left wondering why liftBaseWith can’t just return runInBase. Why does it need to be given a continuation? After all, it would be nicer if we could just write this:

                                            bar' :: MonadBaseControl IO m => m a -> m a -> m a
                                            +bar' ma mb = do
                                            +  runInBase <- askRunInBase
                                            +  restoreM =<< liftBase (bar (runInBase ma) (runInBase mb))

                                            To understand the problem with a hypothetical askRunInBase function, remember that the type of runInBase is polymorphic:

                                            runInBase :: forall a. m a -> b (StM m a)

                                            This is important, since if you need to lift a function with a type like

                                            baz :: IO b -> IO c -> IO (Either b c)

                                            then you’ll want to instantiate that a variable with two different types. We’d need to retain that power in askRunInBase, so it would need to have the following type:

                                            askRunInBase :: MonadBaseControl b m => m (forall a. m a -> b (StM m a))

                                            Sadly, that type is illegal in Haskell. Type constructors must be applied to monomorphic types, but in the above type signature, m is applied to a polymorphic type.2 The RankNTypes GHC extension introduces a single exception: the (->) type constructor is special and may be applied to polymorphic types. That’s why liftBaseWith is legal, but askRunInBase is not: since liftBaseWith is passed a higher-order function that receives runInBase as an argument, the polymorphic type appears immediately under an application of (->), which is allowed.

                                            The aforementioned restriction means we’re basically out of luck, but if you really want askRunInBase, there is a workaround. GHC is perfectly alright with a field of a datatype being polymorphic, so we can define a newtype that wraps a suitably-polymorphic function:

                                            newtype RunInBase b m = RunInBase (forall a. m a -> b (StM m a))

                                            We can now alter askRunInBase to return our newtype, and we can implement it in terms of liftBaseWith:3

                                            askRunInBase :: MonadBaseControl b m => m (RunInBase b m)
                                            +askRunInBase = liftBaseWith $ \runInBase -> pure $ RunInBase runInBase

                                            To use askRunInBase, we have to pattern match on the RunInBase constructor, but it isn’t very noisy, since we can do it directly in a do binding. For example, we could implement a lifted version of baz this way:

                                            baz' :: MonadBaseControl IO m => m a -> m b -> m (Either a b)
                                            +baz' ma mb = do
                                            +  RunInBase runInBase <- askRunInBase
                                            +  s <- liftBase (baz (runInBase ma) (runInBase mb))
                                            +  bitraverse restoreM restoreM s

                                            As of version 1.0.2.3, monad-control does not provide a newtype like RunInBase, so it also doesn’t provide a function like askRunInBase. For now, you’ll have to use liftBaseWith, but it might be a useful future addition to the library.

                                            Pitfalls

                                            At this point in the blog post, we’ve covered the essentials of MonadBaseControl: how it works, how it’s designed, and how you might go about using it. However, so far, we’ve only considered situations where MonadBaseControl works well, and I’ve intentionally avoided examples where the technique breaks down. In this section, we’re going to take a look at the pitfalls and drawbacks of MonadBaseControl, plus some ways they can be mitigated.

                                            No polymorphism, no lifting

                                            All of the pitfalls of MonadBaseControl stem from the same root problem, and that’s the particular technique it uses to save and restore monadic state. We’ll start by considering one of the simplest ways that technique is thwarted, and that’s monomorphism. Consider the following two functions:

                                            poly :: IO a -> IO a
                                            +mono :: IO X -> IO X

                                            Even after all we’ve covered, it may surprise you to learn that although poly can be easily lifted to MonadBaseControl IO m => m a -> m a, it’s impossible to lift mono to MonadBaseControl IO m => m X -> m X. It’s a little unintuitive, as we often think of polymorphic types as being more complicated (so surely lifting polymorphic functions ought to be harder), but in fact, it’s the flexibility of polymorphism that allows MonadBaseControl to work in the first place.

                                            To understand the problem, remember that when we lift a function of type forall a. b a -> b a using MonadBaseControl, we actually instantiate a to (StM m c). That produces a function of type b (StM m c) -> b (StM m c), which is isomorphic to the m c -> m c type we want. The instantiation step is easily overlooked, but it’s crucial, since otherwise we have no way to thread the state through the otherwise opaque function we’re trying to lift!

                                            In the case of mono, that’s exactly the problem we’re faced with. mono will not accept an IO (StM m X) as an argument, only precisely an IO X, so we can’t pass along the monadic state. For all its machinery, MonadBaseControl is no help at all if no polymorphism is involved. Trying to generalize mono without modifying its implementation is a lost cause.

                                            The dangers of discarded state

                                            Our inability to lift mono is frustrating, but at least it’s conclusively impossible. In practice, however, many functions lie in an insidious in-between: polymorphic enough to be lifted, but not without compromises. The simplest of these functions have types such as the following:

                                            sideEffect :: IO a -> IO ()

                                            Unlike mono, it’s entirely possible to lift sideEffect:

                                            sideEffect' :: MonadBaseControl IO m => m a -> m ()
                                            +sideEffect' m = liftBaseWith $ \runInBase -> sideEffect (runInBase m)

                                            This definition typechecks, but you may very well prefer it didn’t, since it has a serious problem: any changes made by m to the monadic state are completely discarded once sideEffect' returns! Since sideEffect' never calls restoreM, there’s no way the state of m can be any different from the original state, but it’s impossible to call restoreM since we don’t actually get an StM m () result from sideEffect.

                                            Sometimes this may be acceptable, since some monad transformers don’t actually have any output state anyway, such as ReaderT r. In other cases, however, sideEffect' could be a bug waiting to happen. One way to make sideEffect' safe would be to add a StM m a ~ a constraint to its context, since that guarantees the monad transformers being lifted through are stateless, and nothing is actually being discarded. Of course, that significantly restricts the set of monad transformers that can be lifted through.

                                            Rewindable state

                                            One scenario where state discarding can actually be useful is operations with so-called rewindable or transactional state. The most common example of such an operation is catch:

                                            catch :: Exception e => IO a -> (e -> IO a) -> IO a

                                            When lifted, state changes from the action or from the exception handler will be “committed,” but never both. If an exception is raised during the computation, those state changes are discarded (“rewound”), giving catch a kind of backtracking semantics. This behavior arises naturally from the way a lifted version of catch must be implemented:

                                            catch' :: (Exception e, MonadBaseControl IO m) => m a -> (e -> m a) -> m a
                                            +catch' m f = do
                                            +  s <- liftBaseWith $ \runInBase ->
                                            +    catch (runInBase m) (runInBase . f)
                                            +  restoreM s

                                            If m raises an exception, it will never return an StM m a value, so there’s no way to get ahold of any of the state changes that happened before the exception. Therefore, the only option is to discard that state.

                                            This behavior is actually quite useful, and it’s definitely not unreasonable. However, useful or not, it’s inconsistent with state changes to mutable values like IORefs or MVars (they stay modified whether an exception is raised or not), so it can still be a gotcha. Either way, it’s worth being aware of.

                                            Partially discarded state

                                            The next function we’re going to examine is finally:

                                            finally :: IO a -> IO b -> IO a

                                            This function has a similar type to catch, and it even has similar semantics. Like catch, finally can be lifted, but unlike catch, its state can’t be given any satisfying treatment. The only way to implement a lifted version is

                                            finally' :: MonadBaseControl IO m => m a -> m b -> m a
                                            +finally' ma mb = do
                                            +  s <- liftBaseWith $ \runInBase ->
                                            +    finally (runInBase ma) (runInBase mb)
                                            +  restoreM s

                                            which always discards all state changes made by the second argument. This is clear just from looking at finally’s type: since b doesn’t appear anywhere in the return type, there’s simply no way to access that action’s result, and therefore no way to access its modified state.

                                            However, don’t despair: there actually is a way to produce a lifted version of finally that preserves all state changes. It can’t be done by lifting finally directly, but if we reimplement finally in terms of simpler lifted functions that are more amenable to lifting, we can produce a lifted version of finally that preserves all the state:4

                                            finally' :: MonadBaseControl IO m => m a -> m b -> m a
                                            +finally' ma mb = mask' $ \restore -> do
                                            +  a <- liftBaseWith $ \runInBase ->
                                            +    try (runInBase (restore ma))
                                            +  case a of
                                            +    Left e -> mb *> liftBase (throwIO (e :: SomeException))
                                            +    Right s -> restoreM s <* mb

                                            This illustrates an important (and interesting) point about MonadBaseControl: whether or not an operation can be made state-preserving is not a fundamental property of the operation’s type, but rather a property of the types of the exposed primitives. There is sometimes a way to implement a state-preserving variant of operations that might otherwise seem unliftable given the right primitives and a bit of cleverness.

                                            Forking state

                                            As a final example, I want to provide an example where the state may not actually be discarded per se, just inaccessible. Consider the type of forkIO:

                                            forkIO :: IO () -> IO ThreadId

                                            Although forkIO isn’t actually polymorphic in its argument, we can convert any IO action to one that produces () via void, so it might as well be. Therefore, we can lift forkIO in much the same way we did with sideEffect:

                                            forkIO' :: MonadBaseControl IO m => m () -> m ThreadId
                                            +forkIO' m = liftBaseWith $ \runInBase -> forkIO (void $ runInBase m)

                                            As with sideEffect, we can’t recover the output state, but in this case, there’s a fundamental reason that goes deeper than the types: we’ve forked off a concurrent computation! We’ve therefore split the state in two, which might be what we want… but it also might not. forkIO is yet another illustration that it’s important to think about the state-preservation semantics when using MonadBaseControl, or you may end up with a bug!

                                            MonadBaseControl in context

                                            Congratulations: you’ve made it through most of this blog post. If you’ve followed everything so far, you now understand MonadBaseControl. All the tricky parts are over. However, before wrapping up, I’d like to add a little extra information about how MonadBaseControl relates to various other parts of the Haskell ecosystem. In practice, that information can be as important as understanding MonadBaseControl itself.

                                            The remainder of monad-control

                                            If you look at the documentation for monad-control, you’ll find that it provides more than just the MonadBaseControl typeclass. I’m not going to cover everything else in detail in this blog post, but I do want to touch upon it briefly.

                                            First off, you should definitely take a look at the handful of helper functions provided by monad-control, such as control and liftBaseOp_. These functions provide support for lifting common function types without having to use liftBaseWith directly. It’s useful to understand liftBaseWith, since it’s the most general way to use MonadBaseControl, but in practice, it is simpler and more readable to use the more specialized functions wherever possible. Many of the examples in this very blog post could be simplified using them, and I only stuck to liftBaseWith to introduce as few new concepts at a time as possible.

                                            Second, I’d like to mention the related MonadTransControl typeclass. You hopefully remember from earlier in the blog post how we defined MonadBaseControl instances inductively so that we could lift all the way down to the base monad. MonadTransControl is like MonadBaseControl if it intentionally did not do that—it allows lifting through a single transformer at a time, rather than through all of them at once.

                                            Usually, MonadTransControl is not terribly useful to use directly (though I did use it once in a previous blog post of mine to help derive instances of mtl-style classes), but it is useful for implementing MonadBaseControl instances for your own transformers. If you define a MonadTransControl instance for your monad transformer, you can get a MonadBaseControl implementation for free using the provided ComposeSt, defaultLiftBaseWith, and defaultRestoreM bindings; see the documentation for more details.

                                            lifted-base and lifted-async

                                            If you’re going to use MonadBaseControl, the lifted-base and lifted-async packages are good to know about. As their names imply, they provide lifted versions of bindings in the base and async packages, so you can use them directly without needing to lift them yourself. For example, if you needed a lifted version of mask from Control.Exception, you could swap it for the mask export from Control.Exception.Lifted, and everything would mostly just work (though always be sure to check the documentation for any caveats on state discarding).

                                            Relationship to MonadUnliftIO

                                            Recently, FP Complete has developed the unliftio package as an alternative to monad-control. It provides the MonadUnliftIO typeclass, which is similar in spirit to MonadBaseControl, but heavily restricted: it is specialized to IO as the base monad, and it only allows instances for stateless monads, such as ReaderT. This is designed to encourage the so-called ReaderT design pattern, which avoids ever using stateful monads like ExceptT or StateT over IO, encouraging the use of IO exceptions and mutable variables (e.g. MVars or TVars) instead.

                                            I should be clear: I really like most of what FP Complete has done—to this day, I still use stack as my Haskell build tool of choice—and I think the suggestions given in the aforementioned “ReaderT design pattern” blog post have real weight to them. I have a deep respect for Michael Snoyman’s commitment to opinionated, user-friendly tools and libraries. But truthfully, I can’t stand MonadUnliftIO.

                                            MonadUnliftIO is designed to avoid all the complexity around state discarding that MonadBaseControl introduces, and on its own, that’s a noble goal. Safety first, after all. The problem is that MonadUnliftIO really is extremely limiting, and what’s more, it can actually be trivially encoded in terms of MonadBaseControl as follows:

                                            type MonadUnliftIO m = (MonadBaseControl IO m, forall a. StM m a ~ a)

                                            This alias can be used to define safe, lifted functions that never discard state while still allowing functions that can be safely lifted through stateful transformers to do so. Indeed, the Control.Concurrent.Async.Lifted.Safe module from lifted-async does exactly that (albeit with a slightly different formulation than the above alias).

                                            To be fair, the unliftio README does address this in its comparison section:

                                            monad-control allows us to unlift both styles. In theory, we could write a variant of lifted-base that never does state discards […] In other words, this is an advantage of monad-control over MonadUnliftIO. We've avoided providing any such extra typeclass in this package though, for two reasons:

                                            • MonadUnliftIO is a simple typeclass, easy to explain. We don't want to complicated [sic] matters […]

                                            • Having this kind of split would be confusing in user code, when suddenly [certain operations are] not available to us.

                                            In other words, the authors of unliftio felt that MonadBaseControl was simply not worth the complexity, and they could get away with MonadUnliftIO. Frankly, if you feel the same way, by all means, use unliftio. I just found it too limiting given the way I write Haskell, plain and simple.

                                            Recap

                                            So ends another long blog post. As often seems the case, I set out to write something short, but I ended up writing well over 5,000 words. I suppose that means I learned something from this experience, too: MonadBaseControl is more complicated than I had anticipated! Maybe there’s something to take away from that.

                                            In any case, it’s over now, so I’d like to briefly summarize what we’ve covered:

                                            • MonadBaseControl allows us to lift higher-order monadic operations.

                                            • It operates by capturing the current monadic state and explicitly threading it through the action in the base monad before restoring it.

                                            • That technique works well for polymorphic operations for the type forall a. b a -> b a, but it can be tricky or even impossible for more complex operations, sometimes leading to discarded state.

                                              This can sometimes be mitigated by restricting certain operations to stateless monads using a StM m a ~ a constraint, or by reimplementing the operation in terms of simpler primitives.

                                            • The lifted-base and lifted-async packages provide lifted versions of existing operations, avoiding the need to lift them yourself.

                                            As with many abstractions in Haskell, don’t worry too much if you don’t have a completely firm grasp of MonadBaseControl at first. Insight often comes with repeated experience, and monad-control can still be used in useful ways even without a perfect understanding. My hope is that this blog post has helped you build intuitions about MonadBaseControl even if some of the underlying machinery remains a little fuzzy, and I hope it can also serve as a reference for those who want or need to understand (or just be reminded of) all the little details.

                                            Finally, I’ll admit MonadBaseControl isn’t especially elegant or beautiful as Haskell abstractions go. In fact, in many ways, it’s a bit of a kludge! Perhaps, in time, effect systems will evolve and mature so that it and its ilk are no longer necessary, and they may become distant relics of an inferior past. But in the meantime, it’s here, it’s useful, and I think it’s worth embracing. If you’ve shied away from it in the past, I hope I’ve illuminated it enough to make you consider giving it another try.

                                            1. One example of a function with that type is mask_.

                                            2. Types with polymorphic types under type constructors are called impredicative. GHC technically has limited support for impredicativity via the ImpredicativeTypes language extension, but as of GHC 8.8, it has been fairly broken for some time. A fix is apparently being worked on, but even if that effort is successful, I don’t know what impact it will have on type inference.

                                            3. Note that askRunInBase = liftBaseWith (pure . RunInBase) does not typecheck, as it would require impredicative polymorphism: it would require instantiating the type of (.) with polymorphic types. The version using ($) works because GHC actually has special typechecking rules for ($)! Effectively, f $ x is really syntax in GHC.

                                            4. Assume that mask' is a suitably lifted version of mask (which can in fact be made state-preserving).

                                            \ No newline at end of file diff --git a/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/index.html b/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/index.html new file mode 100644 index 0000000..edc384b --- /dev/null +++ b/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/index.html @@ -0,0 +1 @@ +Empathy and subjective experience in programming languages

                                            Empathy and subjective experience in programming languages

                                            A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.

                                            Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?

                                            I think about that question a lot.

                                            2015 called, and they want their dress back

                                            Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.

                                            However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into Star Wars. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.1

                                            There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.

                                            Take 2015’s phenomenon of “the dress” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did this—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.

                                            When something objective isn’t

                                            Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.

                                            The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.

                                            In the case of the dress, whether you heard “yanny” or “laurel,” or whether you believe the Sonic games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?

                                            I think the answer is absolutely, unequivocally yes.

                                            Subjectivity in programming, and in programming languages specifically

                                            Quick question: which is better, functional or imperative programming?

                                            My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?

                                            Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?

                                            I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,2 but I did happen to find a link to a recent discussion on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?

                                            In a branch of the discussion, one user writes:

                                            Haskell is great for business and great in production

                                            I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.

                                            I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.

                                            The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:

                                            I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.

                                            […]

                                            From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.

                                            Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:

                                            Haskell gives one plenty of rope to hang himself on complexity.

                                            So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.

                                            Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:

                                            Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.

                                            […]

                                            The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.

                                            Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.

                                            That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?

                                            The unsatisfying subjective reality of programming languages

                                            You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?

                                            No!

                                            These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, why? Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.

                                            While one commenter in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” another says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.

                                            Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t get it. There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.

                                            Empathy, and how bad results come from good intentions

                                            I’ll admit that these kinds of discussions aren’t always fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.

                                            Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.

                                            Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:

                                            Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.

                                            I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?

                                            If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.

                                            Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.

                                            To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:

                                            It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.

                                            Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.

                                            When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective without invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about your own experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.

                                            It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.

                                            Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be happier.

                                            1. This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is blah,” but you deserve better. So you, uh, get a meta snarky footnote instead.

                                            2. Which, to be entirely fair, may well be as subjective as anything else in this blog post.

                                            \ No newline at end of file diff --git a/blog/2019/11/05/parse-don-t-validate/index.html b/blog/2019/11/05/parse-don-t-validate/index.html new file mode 100644 index 0000000..735e3c8 --- /dev/null +++ b/blog/2019/11/05/parse-don-t-validate/index.html @@ -0,0 +1,39 @@ +Parse, don’t validate

                                            Parse, don’t validate

                                            Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.

                                            However, about a month ago, I was reflecting on Twitter about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:

                                            Parse, don’t validate.

                                            The essence of type-driven design

                                            Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.

                                            The realm of possibility

                                            One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:

                                            foo :: Integer -> Void

                                            Is it possible to implement foo? Trivially, the answer is no, as Void is a type that contains no values, so it’s impossible for any function to produce a value of type Void.1 That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:

                                            head :: [a] -> a

                                            This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:

                                            head :: [a] -> a
                                            +head (x:_) = x
                                            warning: [-Wincomplete-patterns]
                                            +    Pattern match(es) are non-exhaustive
                                            +    In an equation for ‘head’: Patterns not matched: []
                                            +

                                            This message is helpfully pointing out that our function is partial, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is [], the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.

                                            Turning partial functions total

                                            To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the head function, and we’ll start with the simplest one.

                                            Managing expectations

                                            As established, head is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the Maybe type:

                                            head :: [a] -> Maybe a

                                            This buys us the freedom we need to implement head—it allows us to return Nothing when we discover we can’t produce a value of type a after all:

                                            head :: [a] -> Maybe a
                                            +head (x:_) = Just x
                                            +head []    = Nothing

                                            Problem solved, right? For the moment, yes… but this solution has a hidden cost.

                                            Returning Maybe is undoubtably convenient when we’re implementing head. However, it becomes significantly less convenient when we want to actually use it! Since head always has the potential to return Nothing, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:

                                            getConfigurationDirectories :: IO [FilePath]
                                            +getConfigurationDirectories = do
                                            +  configDirsString <- getEnv "CONFIG_DIRS"
                                            +  let configDirsList = split ',' configDirsString
                                            +  when (null configDirsList) $
                                            +    throwIO $ userError "CONFIG_DIRS cannot be empty"
                                            +  pure configDirsList
                                            +
                                            +main :: IO ()
                                            +main = do
                                            +  configDirs <- getConfigurationDirectories
                                            +  case head configDirs of
                                            +    Just cacheDir -> initializeCache cacheDir
                                            +    Nothing -> error "should never happen; already checked configDirs is non-empty"

                                            When getConfigurationDirectories retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use head in main to get the first element of the list, the Maybe FilePath result still requires us to handle a Nothing case that we know will never happen! This is terribly bad for several reasons:

                                            1. First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?

                                            2. Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.

                                            3. Finally, and worst of all, this code is a bug waiting to happen! What if getConfigurationDirectories were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update main, and suddenly the “impossible” error becomes not only possible, but probable.

                                            The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically prove the Nothing case impossible, then a modification to getConfigurationDirectories that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.

                                            Paying it forward

                                            Clearly, our modified version of head leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, head should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?

                                            Let’s look at the original (partial) type signature for head again:

                                            head :: [a] -> a

                                            The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, [a]). Instead of weakening the return type, we can strengthen the argument type, eliminating the possibility of head ever being called on an empty list in the first place.

                                            To do this, we need a type that represents non-empty lists. Fortunately, the existing NonEmpty type from Data.List.NonEmpty is exactly that. It has the following definition:

                                            data NonEmpty a = a :| [a]

                                            Note that NonEmpty a is really just a tuple of an a and an ordinary, possibly-empty [a]. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the [a] component is [], the a component must always be present. This makes head completely trivial to implement:2

                                            head :: NonEmpty a -> a
                                            +head (x:|_) = x

                                            Unlike before, GHC accepts this definition without complaint—this definition is total, not partial. We can update our program to use the new implementation:

                                            getConfigurationDirectories :: IO (NonEmpty FilePath)
                                            +getConfigurationDirectories = do
                                            +  configDirsString <- getEnv "CONFIG_DIRS"
                                            +  let configDirsList = split ',' configDirsString
                                            +  case nonEmpty configDirsList of
                                            +    Just nonEmptyConfigDirsList -> pure nonEmptyConfigDirsList
                                            +    Nothing -> throwIO $ userError "CONFIG_DIRS cannot be empty"
                                            +
                                            +main :: IO ()
                                            +main = do
                                            +  configDirs <- getConfigurationDirectories
                                            +  initializeCache (head configDirs)

                                            Note that the redundant check in main is now completely gone! Instead, we perform the check exactly once, in getConfigurationDirectories. It constructs a NonEmpty a from a [a] using the nonEmpty function from Data.List.NonEmpty, which has the following type:

                                            nonEmpty :: [a] -> Maybe (NonEmpty a)

                                            The Maybe is still there, but this time, we handle the Nothing case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a NonEmpty FilePath value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type NonEmpty a as being like a value of type [a], plus a proof that the list is non-empty.

                                            By strengthening the type of the argument to head instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:

                                            • The code has no redundant checks, so there can’t be any performance overhead.

                                            • Furthermore, if getConfigurationDirectories changes to stop checking that the list is non-empty, its return type must change, too. Consequently, main will fail to typecheck, alerting us to the problem before we even run the program!

                                            What’s more, it’s trivial to recover the old behavior of head from the new one by composing head with nonEmpty:

                                            head' :: [a] -> Maybe a
                                            +head' = fmap head . nonEmpty

                                            Note that the inverse is not true: there is no way to obtain the new version of head from the old one. All in all, the second approach is superior on all axes.

                                            The power of parsing

                                            You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:

                                            validateNonEmpty :: [a] -> IO ()
                                            +validateNonEmpty (_:_) = pure ()
                                            +validateNonEmpty [] = throwIO $ userError "list cannot be empty"
                                            +
                                            +parseNonEmpty :: [a] -> IO (NonEmpty a)
                                            +parseNonEmpty (x:xs) = pure (x:|xs)
                                            +parseNonEmpty [] = throwIO $ userError "list cannot be empty"

                                            These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: validateNonEmpty always returns (), the type that contains no information, but parseNonEmpty returns NonEmpty a, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but parseNonEmpty gives the caller access to the information it learned, while validateNonEmpty just throws it away.

                                            These two functions elegantly illustrate two different perspectives on the role of a static type system: validateNonEmpty obeys the typechecker well enough, but only parseNonEmpty takes full advantage of it. If you see why parseNonEmpty is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of parseNonEmpty’s name. Is it really parsing anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe parseNonEmpty is a bona-fide parser (albeit a particularly simple one).

                                            Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and parseNonEmpty is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.

                                            Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:

                                            • The aeson library provides a Parser type that can be used to parse JSON data into domain types.

                                            • Likewise, optparse-applicative provides a set of parser combinators for parsing command-line arguments.

                                            • Database libraries like persistent and postgresql-simple have a mechanism for parsing values held in an external data store.

                                            • The servant ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.

                                            The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.

                                            One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the NonEmpty example above: if the parsing and processing logic go out of sync, the program will fail to even compile.

                                            The danger of validation

                                            Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?

                                            Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the language-theoretic security field calls shotgun parsing. In the 2016 paper, The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them, its authors provide the following definition:

                                            Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.

                                            They go on to explain the problems inherent to such validation techniques:

                                            Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.

                                            In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.

                                            It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.

                                            Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.

                                            Parsing, not validating, in practice

                                            So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”

                                            My advice: focus on the datatypes.

                                            Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:

                                            checkNoDuplicateKeys :: (MonadError AppError m, Eq k) => [(k, v)] -> m ()

                                            However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a Map. Adjust your function’s type signature to accept a Map instead of a list of tuples, and implement it as you normally would.

                                            Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to Map, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of checkNoDuplicateKeys:

                                            checkNoDuplicateKeys :: (MonadError AppError m, Eq k) => [(k, v)] -> m (Map k v)

                                            Now the check cannot be omitted, since its result is actually necessary for the program to proceed!

                                            This hypothetical scenario highlights two simple ideas:

                                            1. Use a data structure that makes illegal states unrepresentable. Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.

                                            2. Push the burden of proof upward as far as possible, but no further. Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before any of the data is acted upon.3

                                              If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.

                                            In other words, write functions on the data representation you wish you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!

                                            Here are a handful of additional points of advice, arranged in no particular order:

                                            • Let your datatypes inform your code, don’t let your code control your datatypes. Avoid the temptation to just stick a Bool in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.

                                            • Treat functions that return m () with deep suspicion. Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.

                                            • Don’t be afraid to parse data in multiple passes. Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.

                                            • Avoid denormalized representations of data, especially if it’s mutable. Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.

                                              • Keep denormalized representations of data behind abstraction boundaries. If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.

                                            • Use abstract datatypes to make validators “look like” parsers. Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract newtype with a smart constructor to “fake” a parser from a validator.

                                            As always, use your best judgement. It probably isn’t worth breaking out singletons and refactoring your entire application just to get rid of a single error "impossible" call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.

                                            Recap, reflection, and related reading

                                            That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.

                                            None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about process. My hope is that this is a small step in that direction.

                                            Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post Type Safety Back and Forth. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper Ghosts of Departed Proofs, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.

                                            As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.

                                            1. Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit any value. These aren’t “real” values (unlike null in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that Fast and Loose Reasoning is Morally Correct.

                                            2. In fact, Data.List.NonEmpty already provides a head function with this type, but just for the sake of illustration, we’ll reimplement it ourselves.

                                            3. Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system.

                                            \ No newline at end of file diff --git a/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/index.html b/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/index.html new file mode 100644 index 0000000..a62c061 --- /dev/null +++ b/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/index.html @@ -0,0 +1,65 @@ +No, dynamic type systems are not inherently more open

                                            No, dynamic type systems are not inherently more open

                                            Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.

                                            This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are not about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.

                                            Two typing fallacies

                                            I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to my previous blog post. Two comments in particular caught my eye, the first of which was posted on /r/programming:

                                            Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.

                                            This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.

                                            Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.

                                            The second comment was left on Hacker News, and it is significantly shorter than the first one:

                                            What would be the type signature of, say, Python's pickle.load()?

                                            This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.

                                            Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages can process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.

                                            You can’t process what you don’t know

                                            The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.

                                            The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or EDN.

                                            As a simple example, a login service might emit an event like this one whenever a new user signs up:

                                            {
                                            +  "event_type": "signup",
                                            +  "timestamp": "2020-01-19T05:37:09Z",
                                            +  "data": {
                                            +    "user": {
                                            +      "id": 42,
                                            +      "name": "Alyssa",
                                            +      "email": "alyssa@example.com"
                                            +    }
                                            +  }
                                            +}

                                            Some downstream services might listen for these signup events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:

                                            const handleEvent = ({ event_type, data }) => {
                                            +  switch (event_type) {
                                            +    case 'login':
                                            +      /* ... */
                                            +      break
                                            +    case 'signup':
                                            +      sendEmail(data.user.email, `Welcome to Blockchain Emporium, ${data.user.name}!`)
                                            +      break
                                            +  }
                                            +}

                                            But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who parse, not validate, the Haskell code might look something like this, instead:

                                            data Event = Login LoginPayload | Signup SignupPayload
                                            +data LoginPayload = LoginPayload { userId :: Int }
                                            +data SignupPayload = SignupPayload
                                            +  { userId :: Int
                                            +  , userName :: Text
                                            +  , userEmail :: Text }
                                            +
                                            +instance FromJSON Event where
                                            +  parseJSON = withObject "Event" \obj -> do
                                            +    eventType <- obj .: "event_type"
                                            +    case eventType of
                                            +      "login" -> Login <$> (obj .: "data")
                                            +      "signup" -> Signup <$> (obj .: "signup")
                                            +      _ -> fail $ "unknown event_type: " <> eventType
                                            +
                                            +instance FromJSON LoginPayload where { ... }
                                            +instance FromJSON SignupPayload where { ... }
                                            +
                                            +handleEvent :: JSON.Value -> IO ()
                                            +handleEvent payload = case fromJSON payload of
                                            +  Success (Login LoginPayload { userId }) -> {- ... -}
                                            +  Success (Signup SignupPayload { userName, userEmail }) ->
                                            +    sendEmail userEmail $ "Welcome to Blockchain Emporium, " <> userName <> "!"
                                            +  Error message -> fail $ "could not parse event: " <> message

                                            It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The real problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the Event datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.

                                            In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the switch and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.

                                            Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the Event type is that we wrote handleEvent that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:

                                            const handleEvent = ({ event_type, data }) => {
                                            +  switch (event_type) {
                                            +    /* ... */
                                            +    default:
                                            +      throw new Error(`unknown event_type: ${event_type}`)
                                            +  }
                                            +}

                                            We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:

                                            handleEvent :: JSON.Value -> IO ()
                                            +handleEvent payload = case fromJSON payload of
                                            +  {- ... -}
                                            +  Error _ -> pure ()

                                            This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we do care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.

                                            This illustrates an important point: the Event type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.

                                            This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:

                                            • It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the timestamp field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!

                                            • What’s more, it turns out the Haskell code doesn’t actually use the userId field inside the SignupPayload type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.

                                            • Finally, we neatly avoid all the gotchas related to shotgun parsing mentioned in the previous blog post, since we still haven’t compromised on any of those principles.

                                            We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.

                                            The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an event_type field, and it assumes signup payloads include data.user.name and data.user.email fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.

                                            Keeping opaque data opaque

                                            In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.

                                            Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:

                                            const handleEvent = (payload) => {
                                            +  const signedPayload = { ...payload, signature: signature(payload) }
                                            +  retransmitEvent(signedPayload)
                                            +}

                                            In this case, we don’t care about the structure of the payload at all (the signature function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?

                                            Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:

                                            handleEvent :: JSON.Value -> IO ()
                                            +handleEvent (Object payload) = do
                                            +  let signedPayload = Map.insert "signature" (signature payload) payload
                                            +  retransmitEvent signedPayload
                                            +handleEvent payload = fail $ "event payload was not an object " <> show payload

                                            In this case, since we don’t care about the structure of the payload, we manipulate a value of type JSON.Value directly. This type is extremely imprecise compared to our Event type from earlier—it can hold any legal JSON value, of any shape—but in this case, we want it to be imprecise.

                                            Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.

                                            Once more, note that the assumption we were forced to make explicit in Haskell is also made by the JavaScript code! If our JavaScript handleEvent function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:

                                            > { ..."payload", signature: "sig" }
                                            +{0: "p", 1: "a", 2: "y", 3: "l", 4: "o", 5: "a", 6: "d", signature: "sig"}

                                            Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the Object case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.


                                            Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a UUID type:

                                            type UserId = UUID

                                            However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?

                                            Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a UserId is to define a new, opaque type:

                                            newtype UserId = UserId Text
                                            +  deriving (Eq, FromJSON, ToJSON)

                                            Unlike the type alias defined above which simply creates a new name for the existing UUID type, this declaration creates a totally new UserId type that is distinct from all other types, including Text. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the only way to produce a UserId will be to go through its FromJSON parser. Dually, the only things you can do with a UserId are compare it with other UserIds for equality or serialize it using the ToJSON instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.

                                            This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a UserId is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new UserId out of thin air from an arbitrary string.1

                                            The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.

                                            Reflection is not special

                                            We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What is the type of Python’s pickle.load()? For those unfamiliar, Python’s cutely-named pickle library allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using pickle.dump(), and it can be deserialized at a later point in time using pickle.load().

                                            What makes this appear challenging to our static type system is that the type of value produced by pickle.load() is difficult to predict—it depends entirely on whatever happened to be written to that file using pickle.dump(). This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.

                                            However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens after a program calls pickle.load(). Say you write the following function:

                                            def load_value(f):
                                            +  val = pickle.load(f)
                                            +  # do something with `val`

                                            The trouble is that val can now be of any type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing pickle.load(f) returned—and it turns out those assumptions are val’s type!

                                            For example, imagine the only thing you do with val is call the val.foo() method and return its result, which is expected to be a string. If we were writing Java, then the expected type of val would be quite straightforward—we’d expect it to be an instance of the following interface:

                                            interface Foo extends Serializable {
                                            +  String foo();
                                            +}

                                            And indeed, it turns out a pickle.load()-like function can be given a perfectly reasonable type in Java:

                                            static <T extends Serializable> Optional<T> load(InputStream in, Class<? extends T> cls);

                                            Nitpickers will complain that this isn’t the same as pickle.load(), since you have to pass a Class<T> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing Serializable.class and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do anything with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.


                                            Can we do this in Haskell, too? Absolutely—we can use the serialise library, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to the Haskell JSON library, aeson, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.

                                            That said, while you can emulate the dynamic typing of pickle.load() if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because you wrote the code. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.

                                            This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors is a value’s type.

                                            Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems do impose restrictions on program structure, as it is provably impossible to reject all bad programs in a Turing-complete language without also rejecting some good ones (this is Rice’s theorem). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.

                                            Appendix: the reality behind the myths

                                            The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.

                                            However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.

                                            Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).

                                            These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.

                                            In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term nominal typing. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.

                                            This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:

                                            1. It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but impossible to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.

                                            2. It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.

                                            For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.

                                            If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would not recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.2

                                            Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the real reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)

                                            As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is not productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.

                                            1. Technically, you could abuse the FromJSON instance to convert an arbitrary string to a UserId, but this would not be as easy as it sounds, since fromJSON can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so).

                                            2. I consider this to be Haskell’s most significant flaw at the time of this writing.

                                            \ No newline at end of file diff --git a/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/index.html b/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/index.html new file mode 100644 index 0000000..1f2387b --- /dev/null +++ b/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/index.html @@ -0,0 +1,34 @@ +Types as axioms, or: playing god with static types

                                            Types as axioms, or: playing god with static types

                                            Just what exactly is a type?

                                            A common perspective is that types are restrictions. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t really predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.

                                            But that is not the only perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves you.

                                            …no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.

                                            Seeing the types half-empty

                                            Let’s talk a little about TypeScript.

                                            TypeScript is a gradually-typed language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to gradually add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.

                                            Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations1 can accept any JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.

                                            Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like string | number clearly includes more values than just number, so number is a more restrictive type—a subtype.

                                            An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:

                                            function getFirst(arr: number[]): number | undefined {
                                            +  return arr[0];
                                            +}

                                            If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write getFirst(["hello", "world"]), the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:

                                            function emptyLike(val: number | string): number | string {
                                            +  if (typeof val === "number") {
                                            +    return 0;
                                            +  } else {
                                            +    return "";
                                            +  }
                                            +}

                                            Now if we write emptyLike(42) * 10, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.

                                            When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.

                                            At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of emptyLike to any. “If it can’t even figure this out, can it really be all that useful?”

                                            Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:

                                            • Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.

                                            • Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.

                                            • Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of typeof checks) that obscure the rules the typechecker follows and make them seem semi-magical.

                                            • Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.

                                            • All this frustration breeds a readiness to override the typechecker using casts or any, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.

                                            The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.

                                            Taking back types

                                            After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.

                                            Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:

                                            • Haskell does not have subtyping,2 which means that every value belongs to exactly one type.

                                            • While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.3

                                            • In particular, Haskell is built around the idea that datatypes can be defined with multiple cases, and branching is done via pattern-matching (more on this shortly).

                                            Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:

                                            data Season = Spring | Summer | Fall | Winter

                                            If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named Season with four possible values, Spring, Summer, Fall, and Winter.

                                            But what exactly are those values?

                                            • In TypeScript, we’d represent this type with a union of strings, like this:

                                              type Season = "spring" | "summer" | "fall" | "winter";

                                              Here, Season is a type that can be one of those four strings, but nothing else.

                                            • In C, we’d represent this type with an enum, like this:

                                              enum season { SPRING, SUMMER, FALL, WINTER };

                                              Here, SPRING, SUMMER, FALL, and WINTER are essentially defined to be global aliases for the integers 0, 1, 2, and 3, and the type enum season is essentially an alias for int.

                                            So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply are.

                                            The Haskell declaration invents four completely new constants out of thin air, Spring, Summer, Fall, and Winter. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, Spring is now a value distinct from all other values, even if someone in a different module were to also use the name Spring. Haskell type declarations let us play god, creating something from nothing.

                                            Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and exactly one thing: we can branch on them. For example, we can write a function that takes a Season as an argument and returns whether or not Christmas occurs during it:

                                            containsChristmas :: Season -> Bool
                                            +containsChristmas season = case season of
                                            +  Spring -> False
                                            +  Summer -> True  -- southern hemisphere
                                            +  Fall   -> False
                                            +  Winter -> True  -- northern hemisphere

                                            case expressions are, to a first approximation, a lot like C-style switch statements (though they can do a lot more than this simple example suggests). Using case, we can also define conversions from our totally unique Season constants to other types, if we want:

                                            seasonToString :: Season -> String
                                            +seasonToString season = case season of
                                            +  Spring -> "spring"
                                            +  Summer -> "summer"
                                            +  Fall   -> "fall"
                                            +  Winter -> "winter"

                                            We can also go the other way around, converting a String to a Season, but if we try, we run into a problem: what do we return for a string like, say, "cheesecake"? In other languages, we might throw an error or return null, but Haskell does not have null, and errors are generally reserved for truly catastrophic failures. What can we do instead?

                                            A particularly naïve solution would be to create a type called MaybeASeason that has two cases—it can be a valid Season, or it can be NotASeason:

                                            data MaybeASeason = IsASeason Season | NotASeason
                                            +
                                            +stringToSeason :: String -> MaybeASeason
                                            +stringToSeason seasonString = case seasonString of
                                            +  "spring" -> IsASeason Spring
                                            +  "summer" -> IsASeason Summer
                                            +  "fall"   -> IsASeason Fall
                                            +  "winter" -> IsASeason Winter
                                            +  _        -> NotASeason

                                            This shows a feature of Haskell datatypes that C-style enums do not have: they aren’t just constants, they can contain other values. A MaybeASeason can be one of five different values: IsASeason Spring, IsASeason Summer, IsASeason Fall, IsASeason Winter, or NotASeason.

                                            In TypeScript, we’d write MaybeASeason more like this:

                                            type MaybeASeason = Season | "not-a-season";

                                            This is kind of nice, because we don’t have to wrap all our Season values with IsASeason like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the IsASeason wrapper to distinguish the value as a MaybeASeason rather than a Season.

                                            Now, you may rightly point out that having to invent a type like MaybeASeason every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like MaybeASeason that works for any underlying type. In Haskell, it looks like this:

                                            data Maybe a = Just a | Nothing

                                            This defines a generic type, where the a in Maybe a is a stand-in for some other type, much like the T in Array<T> in other languages. We can change our stringToSeason function to use Maybe:

                                            stringToSeason :: String -> Maybe Season
                                            +stringToSeason seasonString = case seasonString of
                                            +  "spring" -> Just Spring
                                            +  "summer" -> Just Summer
                                            +  "fall"   -> Just Fall
                                            +  "winter" -> Just Winter
                                            +  _        -> Nothing

                                            Maybe gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.

                                            Positive versus negative space

                                            At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.

                                            In TypeScript, when we write a type declaration like

                                            type Season = "summer" | "spring" | "fall" | "winter";

                                            we are defining a type that can be one of those four strings and nothing else. All the other strings that aren’t one of those four make up Season’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.

                                            Of course, I suspect you don’t really buy this argument. What makes a string like "cheesecake" “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.

                                            Suppose you are writing a TypeScript program, and you want a function that only accepts non-empty arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there is a trick for doing that:

                                            type NonEmptyArray<T> = [T, ...T[]];

                                            Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:

                                            type EvenArray<T> = T[] satisfies (arr => arr.length % 2 === 0);

                                            But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”

                                            But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:4

                                            data List a = Nil | Cons a (List a)

                                            This type might be a bit confusing at first if you have not written any Haskell, since it’s recursive. All of these are valid values of type List Int:

                                            • Nil

                                            • Cons 1 Nil

                                            • Cons 1 (Cons 2 Nil)

                                            • Cons 1 (Cons 2 (Cons 3 Nil))

                                            The recursive nature of Cons is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested Conses we want before we terminate the list with a final Nil.

                                            If we wanted to define an EvenList type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict List to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the positive space of things we want to include?

                                            What do I mean by that? Well, we could define an entirely new type that’s just like List, but we make it impossible to ever include an odd number of elements:

                                            data EvenList a = EvenNil | EvenCons a a (EvenList a)

                                            Here are some valid values of type EvenList Int:

                                            • EvenNil

                                            • EvenCons 1 2 EvenNil

                                            • EvenCons 1 2 (EvenCons 3 4 EvenNil)

                                            Lo and behold, a datatype that can only ever include even numbers of elements!

                                            Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:

                                            type EvenList a = List (a, a)

                                            Now values like Cons (1, 2) (Cons (3, 4) Nil) would be valid values of type EvenList Int, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even constructible.

                                            This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,” and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really are awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.

                                            Types as axiom schemas

                                            So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:

                                            • Instead of thinking about how to restrict, it can be useful to think about how to correctly construct.

                                            • In Haskell, datatype declarations invent new values out of thin air.

                                            • We can represent a lot of different data structures using the incredibly simple framework of “datatypes with several possibilities.”

                                            Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.

                                            In Haskell, when you define a datatype, you’re really defining a new, self-contained set of axioms and inference rules. That is rather abstract, so let’s make it more concrete. Consider the List type again:

                                            data List a = Nil | Cons a (List a)

                                            Viewed as an axiom schema, this type has one axiom and one inference rule:

                                            • The empty list is a list.

                                            • If you have a list, and you add an element to the beginning, the result is also a list.

                                            The axiom is Nil, and the inference rule is Cons. Every list5 is constructed by starting with the axiom, Nil, followed by some number of applications of the inference rule, Cons.

                                            We can take a similar approach when designing the EvenList type. The axiom is the same:

                                            • The empty list is a list with an even number of elements.

                                            But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:

                                            • If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.

                                            This corresponds precisely to our EvenList declaration:

                                            data EvenList a = EvenNil | EvenCons a a (EvenList a)

                                            We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:

                                            • If you have a list, and you add an element to the beginning, the result is a non-empty list.

                                            That inference rule corresponds to the following datatype:

                                            data NonEmptyList a = NonEmptyCons a (List a)

                                            Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:

                                            • Zero is a natural number.

                                            • If you have a natural number, its successor (i.e. that number plus one) is also a natural number.

                                            These are two of the Peano axioms, which can be represented in Haskell as the following datatype:

                                            data Natural = Zero | Succ Natural

                                            Using this type, Zero represents 0, Succ Zero represents 1, Succ (Succ Zero) represents 2, and so on. Just as EvenList allowed us to represent any list with an even number of elements but made other values impossible to even express, this Natural type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.

                                            Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret Zero as 0 and Succ n as n + 1, but that interpretation is not inherent to Natural’s definition—it’s all in our heads! We could choose to interpret Succ n as n - 1 instead, in which case we would only be able to represent non-positive integers, or we could interpret Zero as 1 and Succ n as n * 2, in which case we could only represent powers of two.

                                            I find that people sometimes find this approach troubling, or at least counterintuitive. Is Succ (Succ Zero) really 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called number or int, not think to invent a recursive datatype. And admittedly, the Natural type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.

                                            But in less contrived situations, this approach is practical, and in fact it’s highly useful! The quibble that an EvenList Int isn’t “really” a List Int is rather meaningless, seeing as our definition of List was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.

                                            So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, make your datatypes correct by construction.

                                            “But what if I don’t write Haskell?” And other closing thoughts

                                            I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had only written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.

                                            I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript can do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both EvenList and Natural:

                                            type EvenList<T> = [] | [T, T, EvenList<T>];
                                            +type Natural = "zero" | { succ: Natural };

                                            If anything, the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.” Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.

                                            And in general, that’s great!

                                            Being able to reuse the same data representation is hugely beneficial. Functions like map and filter already exist for ordinary lists/arrays, but a home-grown EvenList type needs its own versions. Passing an EvenList to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are obviously a good thing.

                                            But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of any is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.

                                            Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.

                                            1. Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked any type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically.

                                            2. Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type forall a. a -> a is a subtype of the type Int -> Int. But Haskell does not have anything resembling inheritance (e.g. there is no common Number supertype that includes both Int and Double) nor does it have untagged unions (e.g. the argument to a function cannot be something like Int | String, you must define a wrapper type like data IntOrString = AnInt Int | AString String).

                                            3. Lists, tuples, and strings do technically have special syntax, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty.

                                            4. Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post.

                                            5. Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post.

                                            \ No newline at end of file diff --git a/blog/2020/11/01/names-are-not-type-safety/index.html b/blog/2020/11/01/names-are-not-type-safety/index.html new file mode 100644 index 0000000..7d3d3e3 --- /dev/null +++ b/blog/2020/11/01/names-are-not-type-safety/index.html @@ -0,0 +1,79 @@ +Names are not type safety

                                            Names are not type safety

                                            Haskell programmers spend a lot of time talking about type safety. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published Parse, Don’t Validate as an initial stab towards bridging that gap.

                                            The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s newtype construct. The idea is simple enough—the newtype keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this sounds like a simple and straightforward path to type safety. For example, one might consider using a newtype declaration to define a type for an email address:

                                            newtype EmailAddress = EmailAddress Text

                                            This technique can provide some value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct kind of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.

                                            And names are not type safety.

                                            Intrinsic and extrinsic safety

                                            To illustrate the difference between constructive data modeling (discussed at length in my previous blog post) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:

                                            data OneToFive
                                            +  = One
                                            +  | Two
                                            +  | Three
                                            +  | Four
                                            +  | Five

                                            We could then write some functions to convert between Int and our OneToFive type:

                                            toOneToFive :: Int -> Maybe OneToFive
                                            +toOneToFive 1 = Just One
                                            +toOneToFive 2 = Just Two
                                            +toOneToFive 3 = Just Three
                                            +toOneToFive 4 = Just Four
                                            +toOneToFive 5 = Just Five
                                            +toOneToFive _ = Nothing
                                            +
                                            +fromOneToFive :: OneToFive -> Int
                                            +fromOneToFive One   = 1
                                            +fromOneToFive Two   = 2
                                            +fromOneToFive Three = 3
                                            +fromOneToFive Four  = 4
                                            +fromOneToFive Five  = 5

                                            This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:

                                            newtype OneToFive = OneToFive Int

                                            Just as before, we can provide toOneToFive and fromOneToFive functions, with identical types:

                                            toOneToFive :: Int -> Maybe OneToFive
                                            +toOneToFive n
                                            +  | n >= 1 && n <= 5 = Just $ OneToFive n
                                            +  | otherwise        = Nothing
                                            +
                                            +fromOneToFive :: OneToFive -> Int
                                            +fromOneToFive (OneToFive n) = n

                                            If we put these declarations in their own module and choose not to export the OneToFive constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.

                                            To see why, suppose we write a function that consumes a OneToFive value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:

                                            ordinal :: OneToFive -> Text
                                            +ordinal One   = "first"
                                            +ordinal Two   = "second"
                                            +ordinal Three = "third"
                                            +ordinal Four  = "fourth"
                                            +ordinal Five  = "fifth"

                                            The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an Int—after all, it is an Int. An Int can of course contain many other values besides 1 through 5, so we are forced to add an error case to satisfy the exhaustiveness checker:

                                            ordinal :: OneToFive -> Text
                                            +ordinal n = case fromOneToFive n of
                                            +  1 -> "first"
                                            +  2 -> "second"
                                            +  3 -> "third"
                                            +  4 -> "fourth"
                                            +  5 -> "fifth"
                                            +  _ -> error "impossible: bad OneToFive value"

                                            In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:

                                            • The constructive datatype captures its invariants in such a way that they are accessible to downstream consumers. This frees our ordinal function from worrying about handling illegal values, as they have been made unutterable.

                                            • The newtype wrapper provides a smart constructor that validates the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting Ints.

                                            Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of error has punched a hole right through our type system. If we were to add another constructor to our OneToFive datatype,1 the version of ordinal that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.

                                            All of this is a consequence of the fact that the constructive modeling is intrinsically type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent 6 using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an Int; its meaning is specified extrinsically via the toOneToFive smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.

                                            Revisiting non-empty lists

                                            Our OneToFive datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the NonEmpty datatype I’ve repeatedly highlighted in recent blog posts:

                                            data NonEmpty a = a :| [a]

                                            It may be illustrative to imagine a version of NonEmpty represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:

                                            newtype NonEmpty a = NonEmpty [a]
                                            +
                                            +nonEmpty :: [a] -> Maybe (NonEmpty a)
                                            +nonEmpty [] = Nothing
                                            +nonEmpty xs = Just $ NonEmpty xs
                                            +
                                            +instance Foldable NonEmpty where
                                            +  toList (NonEmpty xs) = xs

                                            Just as with OneToFive, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for NonEmpty was the ability to write a safe version of head, but the newtype version requires another assertion:

                                            head :: NonEmpty a -> a
                                            +head xs = case toList xs of
                                            +  x:_ -> x
                                            +  []  -> error "impossible: empty NonEmpty value"

                                            This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines NonEmpty, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.

                                            Newtypes as tokens

                                            If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes can provide a sort of safety, just a weaker one.

                                            The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a trust boundary where internal invariants are enforced by restricting clients to a safe API.

                                            We can use the NonEmpty example from above to illustrate how this works. We refrain from exporting the NonEmpty constructor, and we provide head and tail operations that we trust to never actually fail:

                                            module Data.List.NonEmpty.Newtype
                                            +  ( NonEmpty
                                            +  , cons
                                            +  , nonEmpty
                                            +  , head
                                            +  , tail
                                            +  ) where
                                            +
                                            +newtype NonEmpty a = NonEmpty [a]
                                            +
                                            +cons :: a -> [a] -> NonEmpty a
                                            +cons x xs = NonEmpty (x:xs)
                                            +
                                            +nonEmpty :: [a] -> Maybe (NonEmpty a)
                                            +nonEmpty [] = Nothing
                                            +nonEmpty xs = Just $ NonEmpty xs
                                            +
                                            +head :: NonEmpty a -> a
                                            +head (NonEmpty (x:_)) = x
                                            +head (NonEmpty [])    = error "impossible: empty NonEmpty value"
                                            +
                                            +tail :: NonEmpty a -> [a]
                                            +tail (NonEmpty (_:xs)) = xs
                                            +tail (NonEmpty [])     = error "impossible: empty NonEmpty value"

                                            Since the only way to construct or consume NonEmpty values is to use the functions in Data.List.NonEmpty.Newtype’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like tokens: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case head and tail, to obtain the values contained within.

                                            This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid NonEmpty [] value. For this reason, the newtype approach to type safety does not on its own constitute a proof that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.2

                                            This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a Generic instance for NonEmpty:

                                            {-# LANGUAGE DeriveGeneric #-}
                                            +
                                            +import GHC.Generics (Generic)
                                            +
                                            +newtype NonEmpty a = NonEmpty [a]
                                            +  deriving (Generic)

                                            However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:

                                            ghci> GHC.Generics.to @(NonEmpty ()) (M1 $ M1 $ M1 $ K1 [])
                                            +NonEmpty []

                                            This is a particularly extreme example, since derived Generic instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived Read instance:

                                            ghci> read @(NonEmpty ()) "NonEmpty []"
                                            +NonEmpty []

                                            To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:

                                            • All invariants must be made clear to maintainers of the trusted module. For simple types, such as NonEmpty, the invariant is self-evident, but for more sophisticated types, comments are not optional.

                                            • Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.

                                            • Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.

                                            • Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.

                                            In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.

                                            In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.

                                            Other newtype use, abuse, and misuse

                                            The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:

                                            • Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the Sum and Product newtypes from Data.Monoid provide useful Monoid instances for numeric types.

                                            • In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The Flip newtype from Data.Bifunctor.Flip is a simple example, flipping the arguments of a Bifunctor so the Functor instance may operate on the other side:

                                              newtype Flip p a b = Flip { runFlip :: p b a }

                                              Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.

                                            • More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a ByteString containing a secret key may be wrapped in a newtype (with a Show instance omitted) to discourage code from accidentally logging or otherwise exposing it.

                                            All of these applications are good ones, but they have little to do with type safety. The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually prevents misuse; any part of the program may inspect the value at any time.

                                            Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:

                                            newtype ArgumentName = ArgumentName { unArgumentName :: GraphQL.Name }
                                            +  deriving ( Show, Eq, FromJSON, ToJSON, FromJSONKey, ToJSONKey
                                            +           , Hashable, ToTxt, Lift, Generic, NFData, Cacheable )

                                            This newtype is useless noise. Functionally, it is completely interchangeable with its underlying Name type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an ArgumentName, since the enclosing field name already makes its role clear.

                                            Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:

                                            • Primarily, types distinguish functional differences between values. A value of type NonEmpty a is functionally distinct from a value of type [a], since it is fundamentally structurally different and permits additional operations. In this sense, types are structural; they describe what values are in the internal world of the programming language.

                                            • Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate Distance and Duration types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.

                                            Note that both these uses are pragmatic; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system is a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like ArgumentName.

                                            If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use Name, but in situations where the different label adds genuine clarity, one can always use a type alias:3

                                            type ArgumentName = GraphQL.Name

                                            Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.

                                            Final thoughts and related reading

                                            I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.

                                            Newtypes are one particular mechanism of defining wrapper types, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.

                                            The catalyst that got me to finally sit down and write this was the recently-published Tagged is not a Newtype. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, Tagged is a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.

                                            Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.

                                            And a name is not type safety.

                                            1. Admittedly rather unlikely given its name, but bear with me through the contrived example.

                                            2. In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice.

                                            3. As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post.

                                            \ No newline at end of file diff --git a/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/index.html b/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/index.html new file mode 100644 index 0000000..cc6b935 --- /dev/null +++ b/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/index.html @@ -0,0 +1,345 @@ +An introduction to typeclass metaprogramming

                                            An introduction to typeclass metaprogramming

                                            Typeclass metaprogramming is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the servant ecosystem), and it is the core mechanism used to implement generic programming via GHC generics. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.

                                            This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does not attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also not a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.

                                            Part 1: Basic building blocks

                                            Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.

                                            To start, we’ll cover the absolute foundations of typeclass metaprogramming.

                                            Typeclasses as functions from types to terms

                                            As its name implies, typeclass metaprogramming (henceforth TMP1) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic == operator via the Eq class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: typeclasses are functions from types to (runtime) terms.

                                            What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called TypeOf:

                                            class TypeOf a where
                                            +  typeOf :: a -> String

                                            The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:

                                            instance TypeOf Bool where
                                            +  typeOf _ = "Bool"
                                            +
                                            +instance TypeOf Char where
                                            +  typeOf _ = "Char"
                                            +
                                            +instance (TypeOf a, TypeOf b) => TypeOf (a, b) where
                                            +  typeOf (a, b) = "(" ++ typeOf a ++ ", " ++ typeOf b ++ ")"

                                            Given these instances, we can observe that they do what we expect in GHCi:

                                            ghci> typeOf (True, 'a')
                                            +"(Bool, Char)"

                                            Note that both the TypeOf Bool and TypeOf Char instances ignore the argument to typeOf altogether. This makes sense, as the whole point of the TypeOf class is to get access to type information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:

                                            {-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}
                                            +
                                            +class TypeOf a where
                                            +  typeOf :: String

                                            This typeclass definition is a little unusual, as the type parameter a doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition

                                            class Show a where
                                            +  show :: a -> String

                                            the full type of the show method is implicitly extended with a Show a constraint to yield:

                                            show :: Show a => a -> String

                                            Furthermore, if we write foralls explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the full type of show:

                                            show :: forall a. Show a => a -> String

                                            In the same vein, we can write out the full type of typeOf, as given by our new definition of TypeOf:

                                            typeOf :: forall a. TypeOf a => String

                                            This type is still unusual, as the a type parameter doesn’t appear anywhere to the right of the => arrow. This makes the type parameter trivially ambiguous, which is to say it’s impossible for GHC to infer what a should be at any call site. Fortunately, we can use TypeApplications to pass a type for a directly, as we can see in the updated definition of TypeOf (a, b):

                                            instance TypeOf Bool where
                                            +  typeOf = "Bool"
                                            +
                                            +instance TypeOf Char where
                                            +  typeOf = "Char"
                                            +
                                            +instance (TypeOf a, TypeOf b) => TypeOf (a, b) where
                                            +  typeOf = "(" ++ typeOf @a ++ ", " ++ typeOf @b ++ ")"

                                            Once again, we can test out our new definitions in GHCi:

                                            ghci> typeOf @Bool
                                            +"Bool"
                                            +ghci> typeOf @(Bool, Char)
                                            +"(Bool, Char)"

                                            This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our typeOf function is, quite literally, a function that accepts a single type as an argument and returns a term-level String. Of course, the TypeOf typeclass is not a particularly useful example of such a function, but it demonstrates how easy it is to construct.

                                            Type-level interpreters

                                            One important consequence of eliminating the value-level argument of typeOf is that there is no need for its argument type to actually be inhabited. For example, consider the TypeOf instance on Void from Data.Void:

                                            instance TypeOf Void where
                                            +  typeOf = "Void"

                                            This above instance is no different from the ones on Bool and Char even though Void is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of TypeOf on lists:

                                            instance TypeOf a => TypeOf [a] where
                                            +  typeOf = "[" ++ typeOf @a ++ "]"

                                            If typeOf required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type a to recursively apply typeOf to. But since typeOf only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.

                                            A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:

                                            data Z
                                            +data S a

                                            It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:

                                            • Z is a type that represents 0.

                                            • S Z is a type that represents 1.

                                            • S (S Z) is a type that represents 2.

                                            And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:

                                            import Numeric.Natural
                                            +
                                            +class ReifyNat a where
                                            +  reifyNat :: Natural
                                            +
                                            +instance ReifyNat Z where
                                            +  reifyNat = 0
                                            +
                                            +instance ReifyNat a => ReifyNat (S a) where
                                            +  reifyNat = 1 + reifyNat @a

                                            As its name implies, reifyNat reifies a type-level natural number encoded using our datatypes above into a term-level Natural value:

                                            ghci> reifyNat @Z
                                            +0
                                            +ghci> reifyNat @(S Z)
                                            +1
                                            +ghci> reifyNat @(S (S Z))
                                            +2

                                            One way to think about reifyNat is as an interpreter of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.

                                            Overlapping instances

                                            Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for Show (Maybe a), you aren’t supposed to also write an instance for Show (Maybe Bool), since it isn’t clear whether show (Just True) should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.

                                            Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.

                                            As a simple example, suppose we wanted to write a typeclass that checks whether a given type is () or not:

                                            class IsUnit a where
                                            +  isUnit :: Bool

                                            If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:

                                            -- not actually valid Haskell, just an example
                                            +isUnit :: * -> Bool
                                            +isUnit () = True
                                            +isUnit _  = False

                                            But if we try to translate this to typeclass instances, we’ll get a problem:

                                            instance IsUnit () where
                                            +  isUnit = True
                                            +
                                            +instance IsUnit a where
                                            +  isUnit = False

                                            The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.2 This means GHC will complain about instance overlap if we try to evaluate isUnit @():

                                            ghci> isUnit @()
                                            +
                                            +error:
                                            +    • Overlapping instances for IsUnit ()
                                            +        arising from a use of ‘isUnit’
                                            +      Matching instances:
                                            +        instance IsUnit a
                                            +        instance IsUnit ()
                                            +

                                            To fix this, we have to explicitly mark IsUnit () as overlapping:

                                            instance {-# OVERLAPPING #-} IsUnit () where
                                            +  isUnit = True

                                            Now GHC accepts the expression without complaint:

                                            ghci> isUnit @()
                                            +True

                                            What does the {-# OVERLAPPING #-} pragma do, exactly? The gory details are spelled out in the GHC User’s Guide, but the simple explanation is that {-# OVERLAPPING #-} relaxes the overlap checker as long as the instance is strictly more specific than the instance(s) it overlaps with. In this case, that is true: IsUnit () is trivially more specific than IsUnit a, since the former only matches () while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.

                                            Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:

                                            guardUnit :: forall a. a -> Either String a
                                            +guardUnit x = case isUnit @a of
                                            +  True  -> Left "unit is not allowed"
                                            +  False -> Right x

                                            The intent of guardUnit is to use isUnit to detect if its argument is of type (), and if it is, to return an error. However, even though we marked IsUnit () overlapping, we still get an overlapping instance error:

                                            error:
                                            +    • Overlapping instances for IsUnit a arising from a use of ‘isUnit’
                                            +      Matching instances:
                                            +        instance IsUnit a
                                            +        instance [overlapping] IsUnit ()
                                            +    • In the expression: isUnit @a
                                            +

                                            What gives? The problem is that GHC simply doesn’t know what type a is when compiling guardUnit. It could be instantiated to () where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.

                                            This behavior is actually a very, very good thing. If GHC were to blindly pick the IsUnit a instance in this case, then guardUnit would always take the False branch, even when passed a value of type ()! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when {-# OVERLAPPING #-} annotations are used, so it’s important to keep their limitations in mind.

                                            As it happens, in this particular case, the error is easily remedied. We simply have to add an IsUnit constraint to the type signature of guardUnit:

                                            guardUnit :: forall a. IsUnit a => a -> Either String a
                                            +guardUnit x = case isUnit @a of
                                            +  True  -> Left "unit is not allowed"
                                            +  False -> Right x

                                            Now picking the right IsUnit instance is deferred to the place where guardUnit is used, and the definition is accepted.3

                                            Type families are functions from types to types

                                            In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:

                                            {-# LANGUAGE TypeFamilies #-}
                                            +
                                            +type family Sum a b where
                                            +  Sum Z     b = b
                                            +  Sum (S a) b = S (Sum a b)

                                            The above is a closed type family, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of Sum would look like this:

                                            data Nat = Z | S Nat
                                            +
                                            +sum :: Nat -> Nat -> Nat
                                            +sum Z     b = b
                                            +sum (S a) b = S (sum a b)

                                            As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.

                                            To test our definition of Sum in GHCi, we can use the :kind! command, which prints out a type and its kind after reducing it as much as possible:

                                            ghci> :kind! Sum (S Z) (S (S Z))
                                            +Sum (S Z) (S (S Z)) :: *
                                            += S (S (S Z))

                                            We can also combine Sum with our ReifyNat class from earlier:

                                            ghci> reifyNat @(Sum (S Z) (S (S Z)))
                                            +3

                                            Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.

                                            Example 1: Generalized concat

                                            Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a flatten function similar to like-named functions provided by many dynamically-typed languages. In those languages, flatten is like concat, but it works on a list of arbitrary depth. For example, we might use it like this:

                                            > flatten [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
                                            +[1, 2, 3, 4, 5, 6, 7, 8]

                                            In Haskell, lists of different depths have different types, so multiple levels of concat have to be applied explicitly. But using TMP, we can write a generic flatten function that operates on lists of any depth!

                                            Since this is typeclass metaprogramming, we’ll unsurprisingly begin with a typeclass:

                                            class Flatten a where
                                            +  flatten :: a -> [???]

                                            Our first challenge is writing the return type of flatten. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:

                                            type family ElementOf a where
                                            +  ElementOf [[a]] = ElementOf [a]
                                            +  ElementOf [a]   = a
                                            +
                                            +class Flatten a where
                                            +  flatten :: a -> [ElementOf a]

                                            Now we can write our Flatten instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:

                                            instance Flatten [a] where
                                            +  flatten x = x

                                            The inductive case is when the type is a nested list, in which case we want to apply concat and recur:

                                            instance {-# OVERLAPPING #-} Flatten [a] => Flatten [[a]] where
                                            +  flatten x = flatten (concat x)

                                            Sadly, if we try to compile these definitions, GHC will reject our Flatten [a] instance:

                                            error:
                                            +    • Couldn't match type ‘a’ with ‘ElementOf [a]’
                                            +      ‘a’ is a rigid type variable bound by
                                            +        the instance declaration
                                            +      Expected type: [ElementOf [a]]
                                            +        Actual type: [a]
                                            +    • In the expression: x
                                            +      In an equation for ‘flatten’: flatten x = x
                                            +      In the instance declaration for ‘Flatten [a]’
                                            +   |
                                            +   |   flatten x = x
                                            +   |               ^
                                            +

                                            At first blush, this error looks very confusing. Why doesn’t GHC think a and ElementOf [a] are the same type? Well, consider what would happen if we picked a type like [Int] for a. Then [a] would be [[Int]], a nested list, so the first case of ElementOf would apply. Therefore, GHC refuses to pick the second equation of ElementOf so hastily.

                                            In this particular case, we might think that’s rather silly. After all, if a were [Int], then GHC wouldn’t have picked the Flatten [a] instance to begin with, it would pick the more specific Flatten [[a]] instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.

                                            Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our Flatten [a] instance:

                                            instance (ElementOf [a] ~ a) => Flatten [a] where
                                            +  flatten x = x

                                            This is a type equality constraint. Type equality constraints are written with the syntax a ~ b, and they state that a must be the same type as b. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that ElementOf [a] must always be a, which allows the instance to typecheck.

                                            Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must eventually be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the Flatten [a] instance is selected, GHC will know that a is not a list type, and it will be able to reduce ElementOf [a] to a without difficulty. Indeed, we can see this for ourselves by using flatten in GHCi:

                                            ghci> flatten [[[1 :: Integer, 2], [3, 4]], [[5, 6], [7, 8]]]
                                            +[1,2,3,4,5,6,7,8]

                                            It works! But why do we need the type annotation on 1? If we leave it out, we get a rather hairy type error:

                                            error:
                                            +    • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’
                                            +      Expected type: [ElementOf [a]]
                                            +        Actual type: [ElementOf [a0]]
                                            +      NB: ‘ElementOf’ is a non-injective type family
                                            +      The type variable ‘a0’ is ambiguous
                                            +

                                            The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a Num [a] instance, in which case 1 could actually have a list type, and either case of ElementOf could match depending on the choice of Num instance. Of course, no such Num instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.

                                            This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call flatten on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.

                                            That wrinkle aside, flatten is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.

                                            Typeclasses as compile-time code generation

                                            Presented with the above definition of Flatten, it might not be immediately obvious how to think about Flatten as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, Eq or Show) than the TypeOf and ReifyNat classes we defined above.

                                            One useful way to shift our perspective is to consider equivalent Flatten instances written using point-free style:

                                            instance (ElementOf [a] ~ a) => Flatten [a] where
                                            +  flatten = id
                                            +
                                            +instance {-# OVERLAPPING #-} Flatten [a] => Flatten [[a]] where
                                            +  flatten = flatten . concat

                                            These definitions of flatten no longer (syntactically) depend on term-level arguments, just like our definitions of typeOf and reifyNat didn’t accept any term-level arguments above. This allows us to consider what flatten might “expand to” given a type argument alone:

                                            • flatten @[Int] is just id, since the Flatten [a] instance is selected.

                                            • flatten @[[Int]] is flatten @[Int] . concat, since the Flatten [[a]] instance is selected. That then becomes id . concat, which can be further simplified to just concat.

                                            • flatten @[[[Int]]] is flatten @[[Int]] . concat, which simplifies to concat . concat by the same reasoning above.

                                            • flatten @[[[[Int]]]] is then concat . concat . concat, and so on.

                                            This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of flatten takes a type as an argument and produces some number of composed concats as a result. From this perspective, Flatten is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.

                                            This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name metaprogramming. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.

                                            Part 2: Generic programming

                                            Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.

                                            In the previous section, we discussed how to use TMP to write a generic flatten operation. In this section, we’ll aim a bit higher: totally generic functions that operate on arbitrary datatypes.

                                            Open type families and associated types

                                            Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, open type families. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using type instance declarations. For example, we could define our Sum family from above like this:

                                            type family Sum a b
                                            +type instance Sum Z b = b
                                            +type instance Sum (S a) b = S (Sum a b)

                                            In the case of Sum, this would not be very useful, and indeed, Sum is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.

                                            This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a Key open type family that relates types to the types used to index them:

                                            type family Key a
                                            +type instance Key (Vector a) = Int
                                            +type instance Key (Map k v) = k
                                            +type instance Key (Trie a) = ByteString

                                            This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:

                                            class HasKey a where
                                            +  hasKey :: Key a -> a -> Bool
                                            +
                                            +instance HasKey (Vector a) where
                                            +  hasKey i vec = i >= 0 && i < Data.Vector.length vec
                                            +
                                            +instance HasKey (Map k v) where
                                            +  hasKey = Data.Map.member
                                            +
                                            +instance HasKey (Trie a) where
                                            +  hasKey = Data.Trie.member

                                            In this case, anyone could define their own data structure, define instances of Key and HasKey for their data structure, and use hasKey to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:

                                            class HasKey a where
                                            +  type Key a
                                            +  hasKey :: Key a -> a -> Bool
                                            +
                                            +instance HasKey (Vector a) where
                                            +  type Key (Vector a) = Int
                                            +  hasKey i vec = i >= 0 && i < Data.Vector.length vec
                                            +
                                            +instance HasKey (Map k v) where
                                            +  type Key (Map k v) = k
                                            +  hasKey = Data.Map.member
                                            +
                                            +instance HasKey (Trie a) where
                                            +  type Key (Trie a) = ByteString
                                            +  hasKey = Data.Trie.member

                                            An open family declared inside a typeclass like this is called an associated type. It works exactly the same way as the separate definitions of Key and HasKey, it just uses a different syntax. Note that although the family and instance keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).

                                            Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like mono-traversable are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.

                                            However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes heavy use of open type families: datatype-generic programming.

                                            Example 2: Datatype-generic programming

                                            Datatype-generic programming refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include

                                            • equality, comparison, and hashing,

                                            • recursive traversal of self-similar data structures, and

                                            • serialization and deserialization,

                                            among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on any datatype.

                                            In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The module documentation for GHC.Generics already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.

                                            Generic datatype representations

                                            At the heart of the Generic class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have

                                            data Authentication
                                            +  = AuthBasic Username Password
                                            +  | AuthSSH PublicKey

                                            then we have a type that is essentially equivalent to this one:

                                            type Authentication = Either (Username, Password) PublicKey

                                            If we know how to define a function on a nested tree built out of Eithers and pairs, then we know how to define it on any such datatype! This is where TMP comes in: recall the way we viewed Flatten as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?

                                            The answer to that question is yes. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, numFields (AuthBasic "alyssa" "pass1234") would return 2, while numFields (AuthSSH "<key>") would return 1. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.

                                            We’ll start by using TMP to implement a “generic” version of numFields that operates on trees of Eithers and pairs as described above:

                                            class GNumFields a where
                                            +  gnumFields :: a -> Natural
                                            +
                                            +-- base case: leaf value
                                            +instance GNumFields a where
                                            +  gnumFields _ = 1
                                            +
                                            +instance {-# OVERLAPPING #-} (GNumFields a, GNumFields b) => GNumFields (a, b) where
                                            +  gnumFields (a, b) = gnumFields a + gnumFields b
                                            +
                                            +instance {-# OVERLAPPING #-} (GNumFields a, GNumFields b) => GNumFields (Either a b) where
                                            +  gnumFields (Left a)  = gnumFields a
                                            +  gnumFields (Right b) = gnumFields b

                                            Just like our Flatten class from earlier, GNumFields uses the type-level structure of its argument to choose what to do:

                                            • If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.

                                            • If we find Left or Right, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.

                                            • In the case of any other value, we’re at a “leaf” in the tree of Eithers and pairs, which corresponds to a single field, so we just return 1.

                                            Now if we call gnumFields (Left ("alyssa", "pass1234")), we’ll get 2, and if we call gnumFields (Right "<key>"), we’ll get 1. All that’s left to do is write a bit of code that converts our Authentication type to a tree of Eithers and pairs:

                                            genericizeAuthentication :: Authentication -> Either (Username, Password) PublicKey
                                            +genericizeAuthentication (AuthBasic user pass) = Left (user, pass)
                                            +genericizeAuthentication (AuthSSH key)         = Right key
                                            +
                                            +numFieldsAuthentication :: Authentication -> Natural
                                            +numFieldsAuthentication = gnumFields . genericizeAuthentication

                                            Now we get the results we want on our Authentication type using numFieldsAuthentication, but we’re not done yet, since it only works on Authentication values. Is there a way to define a generic numFields function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:

                                            class Generic a where
                                            +  type Rep a
                                            +  genericize :: a -> Rep a
                                            +
                                            +instance Generic Authentication where
                                            +  type Rep Authentication = Either (Username, Password) PublicKey
                                            +  genericize (AuthBasic user pass) = Left (user, pass)
                                            +  genericize (AuthSSH key)         = Right key
                                            +
                                            +numFields :: (Generic a, GNumFields (Rep a)) => a -> Natural
                                            +numFields = gnumFields . genericize

                                            Now numFields (AuthBasic "alyssa" "pass1234") returns 2, as desired, and it will also work with any datatype that provides a Generic instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:

                                            • First, we define the Generic class, comprised of two parts:

                                              1. The Rep a associated type maps a type a onto its generic, sums-of-products representation, i.e. one built out of combinations of Either and pairs.

                                              2. The genericize method converts an actual value of type a to the equivalent value using the sums-of-products representation.

                                            • Next, we define a Generic instance for Authentication. Rep Authentication is the sums-of-products representation we described above, and genericize is likewise genericizeAuthentication from above.

                                            • Finally, we define numFields as a function with a GNumFields (Rep a) constraint. This is where all the magic happens:

                                              • When we apply numFields to a datatype, Rep retrieves its generic, sums-of-products representation type.

                                              • The GNumFields class then uses various TMP techniques we’ve already described so far in this blog post to generate a numFields implementation on the fly from the structure of Rep a.

                                              • Finally, that generated numFields implementation is applied to the genericized term-level value, and the result is produced.

                                            After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) numFields operation. Surely just defining numFields on each type directly would be far easier? Indeed, if we were just considering numFields, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined numFields, and all of them would automatically work on Authentication because they all leverage the same Generic instance!

                                            This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation once, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.

                                            Improving our definition of Generic

                                            You may note that the definition of Generic provided above does not match the one in GHC.Generic. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a GHC.Generics tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.

                                            Distinguishing leaves from the spine

                                            One problem with our version of Generic is that it provides no way to distinguish an Either or pair that should be considered a “leaf”, as in a type like this:

                                            data Foo = A (Either Int String) | B (Char, Bool)

                                            Given this type, Rep Foo should be Either (Either Int String) (Char, Bool), and numFields (Right ('a', True)) will erroneously return 2 rather than 1. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:

                                            newtype Leaf a = Leaf { getLeaf :: a }

                                            Now our Generic instances look like this:

                                            instance Generic Authentication where
                                            +  type Rep Authentication = Either (Leaf Username, Leaf Password) (Leaf PublicKey)
                                            +  genericize (AuthBasic user pass) = Left (Leaf user, Leaf pass)
                                            +  genericize (AuthSSH key)         = Right (Leaf key)
                                            +
                                            +instance Generic Foo where
                                            +  type Rep Foo = Either (Leaf (Either Int String)) (Leaf (Char, Bool))
                                            +  genericize (A x) = Left (Leaf x)
                                            +  genericize (B x) = Right (Leaf x)

                                            Since the Leaf constructor now distinguishes a leaf, rather than the absence of an Either or (,) constructor, we’ll have to update our GNumFields instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:

                                            instance GNumFields (Leaf a) where  
                                            +  gnumFields _ = 1
                                            +
                                            +instance (GNumFields a, GNumFields b) => GNumFields (a, b) where
                                            +  gnumFields (a, b) = gnumFields a + gnumFields b
                                            +
                                            +instance (GNumFields a, GNumFields b) => GNumFields (Either a b) where
                                            +  gnumFields (Left a)  = gnumFields a
                                            +  gnumFields (Right b) = gnumFields b

                                            This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.

                                            Handling empty constructors

                                            Suppose we have a type with nullary data constructors, like the standard Bool type:

                                            data Bool = False | True

                                            How do we write a Generic instance for Bool? Using just Either, (,), and Leaf, we can’t, but if we are willing to add a case for (), we can use it to denote nullary constructors:

                                            instance GNumFields () where
                                            +  gnumFields _ = 0
                                            +
                                            +instance Generic Bool where
                                            +  type Rep Bool = Either () ()
                                            +  genericize False = Left ()
                                            +  genericize True  = Right ()

                                            In a similar vein, we could use Void to represent datatypes that don’t have any constructors at all.

                                            Continuing from here

                                            The full version of Generic has a variety of further improvements useful for generic programming, including:

                                            • Support for converting from Rep a to a.

                                            • Special indication of self-recursive datatypes, making generic tree traversals possible.

                                            • Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.

                                            • Fully automatic generation of Generic instances via the DeriveGeneric extension, which reduces the per-type boilerplate to essentially nothing.

                                            The module documentation for GHC.Generics discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.

                                            Part 3: Dependent typing

                                            It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.

                                            Datatype promotion

                                            In part 1, we used uninhabited datatypes like Z and S a to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, True and False:

                                            data True
                                            +data False

                                            Now we could define type families to provide operations on these types, such as Not:

                                            type family Not a where
                                            +  Not True  = False
                                            +  Not False = True

                                            However, this has some frustrating downsides:

                                            • First, it’s simply inconvenient that we have to define these new True and False “dummy” types, which are completely distinct from the Bool type provided by the prelude.

                                            • More significantly, it means Not has a very unhelpful kind:

                                              ghci> :kind Not
                                              +Not :: * -> *

                                              Even though Not is only supposed to be applied to True or False, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like Not Char:

                                              ghci> :kind! Not Char
                                              +Not Char :: *
                                              += Not Char

                                              Rather than getting an error, GHC simply spits Not Char back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.

                                            One way to think about Not is that it is largely dynamically kinded in the same way some languages are dynamically typed. That isn’t entirely true, as we technically will get a kind error if we try to apply Not to a type constructor rather than a type, such as Maybe:

                                            ghci> :kind! Not Maybe
                                            +
                                            +<interactive>:1:5: error:
                                            +    • Expecting one more argument to ‘Maybe’
                                            +      Expected a type, but ‘Maybe’ has kind ‘* -> *’
                                            +

                                            …but * is still a very big kind, much bigger than we would like to permit for Not.

                                            To help with both these problems, GHC provides datatype promotion via the DataKinds language extension. The idea is that for each normal, non-GADT type definition like

                                            data Bool = False | True

                                            then in addition to the normal type constructor and value constructors, GHC also defines several promoted constructors:

                                            • Bool is allowed as both a type and a kind.

                                            • 'True and 'False are defined as new types of kind Bool.

                                            We can see this in action if we remove our data True and data False declarations and adjust our definition of Not to use promoted constructors:

                                            {-# LANGUAGE DataKinds #-}
                                            +
                                            +type family Not a where
                                            +  Not 'True  = 'False
                                            +  Not 'False = 'True

                                            Now the inferred kind of Not is no longer * -> *:

                                            ghci> :kind Not
                                            +Not :: Bool -> Bool

                                            Consequently, we will now get a kind error if we attempt to apply Not to anything other than 'True or 'False:

                                            ghci> :kind! Not Char
                                            +
                                            +<interactive>:1:5: error:
                                            +    • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’
                                            +

                                            This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:

                                            data Nat = Z | S Nat
                                            +
                                            +class ReifyNat (a :: Nat) where
                                            +  reifyNat :: Natural
                                            +
                                            +instance ReifyNat 'Z where
                                            +  reifyNat = 0
                                            +
                                            +instance ReifyNat a => ReifyNat ('S a) where
                                            +  reifyNat = 1 + reifyNat @a

                                            Note that we need to add an explicit kind signature on the definition of the ReifyNat typeclass, since otherwise GHC will assume a has kind *, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that Z and S are related, this prevents someone from coming along and defining a nonsensical instance like ReifyNat Char, which previously would have been allowed but will now be rejected with a kind error.

                                            Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.

                                            GADTs and proof terms

                                            So far in this blog post, we have discussed several different function-like things:

                                            • Ordinary Haskell functions are functions from terms to terms.

                                            • Type families are functions from types to types.

                                            • Typeclasses are functions from types to terms.

                                            A curious reader may wonder about the existence of a fourth class of function:

                                            • ??? are functions from terms to types.

                                            To reason about what could go in the ??? above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?

                                            In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.

                                            GADTs4 are described in detail in the GHC User’s Guide, but the key idea for our purposes is that pattern-matching on a GADT constructor can refine type information. Here’s a simple, silly example:

                                            data WhatIsIt a where
                                            +  ABool :: WhatIsIt Bool
                                            +  AnInt :: WhatIsIt Int
                                            +
                                            +doSomething :: WhatIsIt a -> a -> a
                                            +doSomething ABool x = not x
                                            +doSomething AnInt x = x + 1

                                            Here, WhatIsIt is a datatype with two nullary constructors, ABool and AnInt, similar to a normal, non-GADT datatype like this one:

                                            data WhatIsIt a = ABool | AnInt

                                            What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, ABool and AnInt would both have the type forall a. WhatIsIt a, but in the GADT definition, we explicitly fix a to Bool in the type of ABool and to Int in the type of AnInt.

                                            This simple feature allows us to do very interesting things. The doSomething function is polymorphic in a, but on the right-hand side of the first equation, x has type Bool, while on the right-hand side of the second equation, x has type Int. This is because the WhatIsIt a argument effectively constrains the type of a, as we can see by experimenting with doSomething in GHCi:

                                            ghci> doSomething ABool True
                                            +False
                                            +ghci> doSomething AnInt 10
                                            +11
                                            +ghci> doSomething AnInt True
                                            +
                                            +error:
                                            +    • Couldn't match expected type ‘Int’ with actual type ‘Bool’
                                            +    • In the second argument of ‘doSomething’, namely ‘True’
                                            +      In the expression: doSomething AnInt True
                                            +      In an equation for ‘it’: it = doSomething AnInt True
                                            +

                                            One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The ABool constructor is a proof of a ~ Bool, while the AnInt constructor is a proof of a ~ Int. When you construct ABool or AnInt, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.

                                            GADTs can be much more sophisticated than our simple WhatIsIt type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:

                                            infixr 5 `HCons`
                                            +
                                            +data HList as where
                                            +  HNil  :: HList '[]
                                            +  HCons :: a -> HList as -> HList (a ': as)

                                            This type is a heterogenous list, a list that can contain elements of different types:

                                            ghci> :t True `HCons` "hello" `HCons` 42 `HCons` HNil
                                            +True `HCons` "hello" `HCons` 42 `HCons` HNil
                                            +  :: Num a => HList '[Bool, [Char], a]

                                            An HList is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total head function on HLists like this:

                                            head :: HList (a ': as) -> a
                                            +head (x `HCons` _) = x

                                            Remarkably, GHC does not complain that this definition of head is non-exhaustive. Since we specified that the argument must be of type HList (a ': as) in the type signature for head, GHC knows that the argument cannot be HNil (which would have the type HList '[]), so it doesn’t ask us to handle that case.

                                            These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.

                                            Proofs that work together

                                            This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an HList of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with head, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.

                                            However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a proof term that contains no values, it just encapsulates type equalities on a type-level list:

                                            data OneToThree a b c as where
                                            +  One   :: OneToThree a b c '[a]
                                            +  Two   :: OneToThree a b c '[a, b]
                                            +  Three :: OneToThree a b c '[a, b, c]

                                            We call it a proof term because a value of type OneToThree a b c as constitutes a proof that as has exactly 1, 2, or 3 elements. Using OneToThree, we can write a function that accepts an HList accompanied by a proof term:

                                            sumUpToThree :: OneToThree Int Int Int as -> HList as -> Int
                                            +sumUpToThree One   (x `HCons` HNil)                     = x
                                            +sumUpToThree Two   (x `HCons` y `HCons` HNil)           = x + y
                                            +sumUpToThree Three (x `HCons` y `HCons` z `HCons` HNil) = x + y + z

                                            As with head, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:

                                            • When we match on the OneToThree proof term, information flows from the term level to the type level, refining the type of as in that branch.

                                            • The refined type of as then flows back down to the term level, restricting the shape the HList can take and refinine the set of patterns we have to match.

                                            Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an HList has an even number of elements:

                                            data Even as where
                                            +  EvenNil  :: Even '[]
                                            +  EvenCons :: Even as -> Even (a ': b ': as)

                                            This is a proof which itself has inductive structure: EvenCons takes a proof that as has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an HList:

                                            type family PairUp as where
                                            +  PairUp '[]            = '[]
                                            +  PairUp (a ': b ': as) = (a, b) ': PairUp as
                                            +
                                            +pairUp :: Even as -> HList as -> HList (PairUp as)
                                            +pairUp EvenNil         HNil                     = HNil
                                            +pairUp (EvenCons even) (x `HCons` y `HCons` xs) = (x, y) `HCons` pairUp even xs

                                            Once again, this definition is completely exhaustive, and we can show that it works in GHCi:

                                            ghci> pairUp (EvenCons $ EvenCons EvenNil)
                                            +             (True `HCons` 'a' `HCons` () `HCons` "foo" `HCons` HNil)
                                            +(True,'a') `HCons` ((),"foo") `HCons` HNil

                                            This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.

                                            Proof inference

                                            While our definition of pairUp is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the Even proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.

                                            Remember that typeclasses are functions from types to terms. As its happens, a value of type Even as can be mechanically produced from the structure of the type as. This suggests that we could use TMP to automatically generate Even proofs, and indeed, we can. In fact, it’s not at all complicated:

                                            class IsEven as where
                                            +  evenProof :: Even as
                                            +
                                            +instance IsEven '[] where
                                            +  evenProof = EvenNil
                                            +
                                            +instance IsEven as => IsEven (a ': b ': as) where
                                            +  evenProof = EvenCons evenProof

                                            We can now adjust our pairUp function to use IsEven instead of an explicit Even argument:

                                            pairUp :: IsEven as => HList as -> HList (PairUp as)
                                            +pairUp = go evenProof where
                                            +  go :: Even as -> HList as -> HList (PairUp as)
                                            +  go EvenNil         HNil                     = HNil
                                            +  go (EvenCons even) (x `HCons` y `HCons` xs) = (x, y) `HCons` go even xs

                                            This is essentially identical to its old definition, but by acquiring the proof via IsEven rather than passing it explicitly, we can call pairUp without having to construct a proof manually:

                                            ghci> pairUp (True `HCons` 'a' `HCons` () `HCons` "foo" `HCons` HNil)
                                            +(True,'a') `HCons` ((),"foo") `HCons` HNil

                                            This is rather remarkable. Using TMP, we are able to get GHC to automatically construct a proof that a list is even, with no programmer guidance beyond writing the IsEven typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: IsEven is a function that accepts a type-level list and generates an Even proof term.

                                            From this perspective, typeclasses are a way of specifying a proof search algorithm to the compiler. In the case of IsEven, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.

                                            Aside: GADTs versus type families

                                            Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.

                                            Consider again the type of the pairUp function above (without the typeclass for simplicity):

                                            pairUp :: Even as -> HList as -> HList (PairUp as)

                                            We used both a GADT, Even, and a type family, PairUp. But we could have, in theory, used only a GADT and eliminated the type family altogether. Consider this variation on the Even proof term:

                                            data EvenPairs as bs where
                                            +  EvenNil  :: EvenPairs '[] '[]
                                            +  EvenCons :: EvenPairs as bs -> EvenPairs (a ': b ': as) ((a, b) ': bs)

                                            This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of as as an “input” parameter and bs as an “output” parameter. The idea is that any EvenPairs proof relates both an even-length list type and its paired up equivalent:

                                            • EvenNil has type EvenPairs '[] '[],

                                            • EvenCons EvenNil has type EvenPairs '[a, b] '[(a, b)],

                                            • EvenCons (EvenCons EvenNil) has type EvenPairs '[a, b, c, d] '[(a, b), (c, d)],

                                            • …and so on.

                                            This allows us to reformulate our pairUp type signature this way:

                                            pairUp :: EvenPairs as bs -> HList as -> HList bs

                                            The definition is otherwise unchanged. The PairUp type family is completely gone, because now EvenPairs itself defines the relation. In this way, GADTs can be used like type-level functions!

                                            The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:

                                            import Data.Kind (Constraint)
                                            +
                                            +type family IsEvenTF as :: Constraint where
                                            +  IsEvenTF '[]            = ()
                                            +  IsEvenTF (_ ': _ ': as) = IsEvenTF as

                                            The idea here is that IsEvenTF as produces a constraint can only be satisfied if as has an even number of elements, since that’s the only way it will eventually reduce to (), which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting IsEvenTF as => in a type signature successfully restricts as to be an even-length list, but it doesn’t allow us to write pairUp. To see why, we can try the following definition:

                                            pairUp :: IsEvenTF as => HList as -> HList (PairUp as)
                                            +pairUp HNil                     = HNil
                                            +pairUp (x `HCons` y `HCons` xs) = (x, y) `HCons` pairUp xs

                                            Unlike the version using the GADT, this version of pairUp is not considered exhaustive:

                                            warning: [-Wincomplete-patterns]
                                            +    Pattern match(es) are non-exhaustive
                                            +    In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil
                                            +

                                            This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by IsEvenTF provides no term-level evidence about the shape of as, so we can’t branch on it the way we can branch on the Even GADT.5 (In a sense, IsEvenTF is doing validation, not parsing.)

                                            For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.

                                            Guiding type inference

                                            So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.

                                            However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.

                                            To illustrate what that can look like, suppose we want to use TMP to generate an HList full of () values of an arbitrary length:

                                            class UnitList as where
                                            +  unitList :: HList as
                                            +
                                            +instance UnitList '[] where
                                            +  unitList = HNil
                                            +
                                            +instance UnitList as => UnitList (() ': as) where
                                            +  unitList = () `HCons` unitList

                                            Testing in GHCi, we can see it behaves as desired:

                                            ghci> unitList :: HList '[(), (), ()]
                                            +() `HCons` () `HCons` () `HCons` HNil

                                            Now suppose we write a function that accepts a list containing exactly one element and returns it:

                                            unsingleton :: HList '[a] -> a
                                            +unsingleton (x `HCons` HNil) = x

                                            Naturally, we would expect these to compose without a hitch. If we write unsingleton unitList, our TMP should generate a list of length 1, and we should get back (). However, it may surprise you to learn that isn’t, in fact, what happens:6

                                            ghci> unsingleton unitList
                                            +
                                            +error:
                                            +    • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’
                                            +      prevents the constraint ‘(UnitList '[a0])’ from being solved.
                                            +      Probable fix: use a type annotation to specify what ‘a0’ should be.
                                            +      These potential instances exist:
                                            +        instance UnitList as => UnitList (() : as)
                                            +

                                            What went wrong? The type error says that a0 is ambiguous, but it only lists a single matching UnitList instance—the one we want—so how can it be ambiguous which one to select?

                                            The problem stems from the way we defined UnitList. When we wrote the instance

                                            instance UnitList as => UnitList (() ': as) where

                                            we said the first element of the type-level list must be (), so there’s nothing stopping someone from coming along and defining another instance:

                                            instance UnitList as => UnitList (Int ': as) where
                                            +  unitList = 0 `HCons` unitList

                                            In that case, GHC would have no way to know which instance to pick. Nothing in the type of unsingleton forces the element in the list to have type (), so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.

                                            Of course, this isn’t what we want. The UnitList class is supposed to always return a list of () values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:

                                            instance (a ~ (), UnitList as) => UnitList (a ': as) where
                                            +  unitList = () `HCons` unitList

                                            Here we’ve changed the instance so that it has the shape UnitList (a ': as), with a type variable in place of the (), but we also added an equality constraint that forces a to be (). Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:

                                            ghci> unsingleton unitList
                                            +()

                                            To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:

                                            instance <constraints> => C <types>

                                            The part to the left of the => is known as the instance context, while the part to the right is known as the instance head. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, only the instance head matters, and the instance context is completely ignored. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.

                                            This explains why our two UnitList instances behave differently:

                                            • Given the instance head UnitList (() ': as), GHC won’t select the instance unless it knows the first element of the list is ().

                                            • But given the instance head UnitList (a ': as), GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.

                                            After the UnitList (a ': as) instance is selected, GHC attempts to solve the constraints in the instance context, including the a ~ () constraint. This forces a to be (), resolving the ambiguity and allowing type inference to proceed.

                                            This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:

                                            • If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.

                                            • But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell you what type this is,” effectively giving you a role in type inference itself.

                                            From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.

                                            Given all of the above, consider again the definition of IsEven from earlier:

                                            class IsEven as where
                                            +  evenProof :: Even as
                                            +
                                            +instance IsEven '[] where
                                            +  evenProof = EvenNil
                                            +
                                            +instance IsEven as => IsEven (a ': b ': as) where
                                            +  evenProof = EvenCons evenProof

                                            Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an IsEven (a ': b0) constraint, where b0 is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an IsEven '[a] instance in the future.

                                            To fix this, we can apply the same trick we used for UnitList, just in a slightly different way:

                                            instance (as ~ (b ': bs), IsEven bs) => IsEven (a ': as) where
                                            +  evenProof = EvenCons evenProof

                                            Again, the idea is to move the type information we learn from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can dramatically improve type inference in programs that make heavy use of TMP.

                                            Example 3: Subtyping constraints

                                            At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at Hasura, I had the opportunity to design an internal parser combinator library that captures aspects of the GraphQL type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.

                                            Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our GQLKind datatype has three cases:

                                            data GQLKind
                                            +  = Both
                                            +  | Input
                                            +  | Output

                                            We use DataKind-promoted versions of this GQLKind type as a parameter to a GQLType GADT:

                                            data GQLType k where
                                            +  TScalar      :: GQLType 'Both
                                            +  TInputObject :: InputObjectInfo -> GQLType 'Input
                                            +  TIObject     :: ObjectInfo -> GQLType 'Output
                                            +  -- ...and so on...

                                            This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type GQLType 'Input, we can’t pass a GQLType 'Both, even though we really ought to be able to.

                                            To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:

                                            data SubKind k1 k2 where
                                            +  KRefl :: SubKind k k
                                            +  KBoth :: SubKind 'Both k

                                            The first case, KRefl, states that every kind is trivially a subkind of itself. The second case, KBoth, states that Both is a subkind of any kind at all. (This is a particularly literal example of using a type to define axioms.) The next step is to use TMP to implement proof inference:

                                            class IsSubKind k1 k2 where
                                            +  subKindProof :: SubKind k1 k2
                                            +
                                            +instance IsSubKind 'Both k where
                                            +  subKindProof = KBoth
                                            +
                                            +instance (k ~ 'Input) => IsSubKind 'Input k where
                                            +  subKindProof = KRefl
                                            +
                                            +instance (k ~ 'Output) => IsSubKind 'Output k where
                                            +  subKindProof = KRefl

                                            These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that k is a superkind of 'Input or 'Output, type inference will force them to be equal.

                                            Using IsSubKind, we can easily resolve the problem described above. Rather than write a function with a type like this:

                                            nullable :: GQLParser 'Input a -> GQLParser 'Input (Maybe a)

                                            …we simply use an IsSubKind constraint, instead:

                                            nullable :: IsSubKind k 'Input => GQLParser k a -> GQLParser k (Maybe a)

                                            Now both 'Input and 'Both kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. Consuming the SubKind proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:

                                            type family ParserInput k where
                                            +  ParserInput 'Both   = InputValue
                                            +  ParserInput 'Input  = InputValue
                                            +  ParserInput 'Output = SelectionSet

                                            This type family is used to determine what a GQLParser k a actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that IsSubKind k 'Input implies ParserInput k ~ InputValue.

                                            Fortunately, that is very easy to do using the (:~:) type from Data.Type.Equality in base to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:

                                            data a :~: b where
                                            +  Refl :: a :~: a

                                            Just as with any other GADT, (:~:) can be used to pack up type equalities and unpack them later; a :~: b just happens to be the GADT that corresponds precisely to the equality a ~ b. Using (:~:), we can write a reusable proof that IsSubKind k 'Input implies ParserInput k ~ InputValue:

                                            inputParserInput :: forall k. IsSubKind k 'Input => ParserInput k :~: InputValue
                                            +inputParserInput = case subKindProof @k @'Input of
                                            +  KRefl -> Refl
                                            +  KBoth -> Refl

                                            This function is a very simple proof by cases, where Refl can be read as “Q.E.D.”:

                                            • In the first case, matching on KRefl refines k to 'Input, and ParserInput 'Input is InputValue by definition of ParserInput.

                                            • Likewise, in the second case, matching on KBoth refines k to 'Both, and ParserInput 'Both is also InputValue by definition of ParserInput.

                                            This inputParserInput helper allows functions like nullable, which internally need ParserInput k ~ InputValue, to take the form

                                            nullable :: forall k a. IsSubKind k 'Input => GQLParser k a -> GQLParser k (Maybe a)
                                            +nullable parser = case inputParserInput @k of
                                            +  Refl -> {- ...implementation goes here... -}

                                            Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without IsSubKind doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!

                                            Wrapping up and closing thoughts

                                            So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:

                                            • Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.

                                            • Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.

                                            • Though I’ve called the technique “typeclass metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.

                                            • Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.

                                            The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.

                                            Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.

                                            It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:

                                            • As mentioned earlier in this blog post, the GHC.Generics module documentation is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.

                                            • I have long believed that the GHC User’s Guide is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.

                                            • Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the singletons library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, the accompanying paper is definitely worth a read if you’d like to go down that route.)

                                            Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.

                                            1. Not to be confused with C++’s template metaprogramming, though there are significant similarities between the two techniques.

                                            2. There have been proposals to introduce ordered instances, known in the literature as instance chains, but as of this writing, GHC does not implement them.

                                            3. Note that this also preserves an important property of the Haskell type system, parametricity. A function like id :: a -> a shouldn’t be allowed to do different things depending on which type is chosen for a, which our first version of guardUnit tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity.

                                            4. Short for generalized algebraic datatypes, which is a rather unhelpful name for actually understanding what they are or what they’re for.

                                            5. If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for IsEvenTF:

                                              type family IsEvenTF as :: Constraint where
                                              +  IsEvenTF '[]       = ()
                                              +  IsEvenTF (a ': as) = exists b as'. (as ~ (b ': as'), IsEvenTF as')

                                              The type refinement provided by matching on HCons would be enough for the second case of IsEvenTF to be selected, which would provide an equality proof that as has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all.

                                            6. Actually, I’ve cheated a little bit here, because unsingleton unitList really does typecheck in GHCi under normal circumstances. That’s because the ExtendedDefaultRules extension is enabled in GHCi by default, which defaults ambiguous type variables to (), which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned ExtendedDefaultRules off to illustrate the problem.

                                            \ No newline at end of file diff --git a/blog/2025/05/29/a-break-from-programming-languages/index.html b/blog/2025/05/29/a-break-from-programming-languages/index.html new file mode 100644 index 0000000..1540689 --- /dev/null +++ b/blog/2025/05/29/a-break-from-programming-languages/index.html @@ -0,0 +1 @@ +A break from programming languages

                                            A break from programming languages

                                            This is a blog post I have been considering writing for a long time.

                                            People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.

                                            My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.

                                            This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for me, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why not—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.

                                            Whence programming languages

                                            I have had a personal fascination with programming languages for as long as I have been using them. I made my first serious forays into programming in the fifth grade, writing (very simple and mostly very bad) Flash games in ActionScript 3. I wrote my first programming language interpreter in my sophomore year of high school, a dynamically-typed, object-oriented Lisp with prototypal inheritance. The interpreter was written in C, and despite it being my first serious endeavor with the language, I used preprocessor macros to implement a form of exceptions in terms of setjmp.h. It is an amusing project in retrospect, given what my design sensibilities would eventually turn out to be, but it at least provides a little insight into how much the subject was already on my mind in my teenage years.

                                            I have always deeply enjoyed programming. That is no less true now than it was fifteen years ago. As someone with (then untreated) ADHD, the automation of repetitive tasks has always held immediate appeal, but it did not take long to discover I enjoy programming for its own sake. It is inherently delightful to me to direct an infinitely reconfigurable machine towards any end I desire, given only a little thought and some time at a keyboard. Program design, even in the large, was something that immediately appealed to me. It is often as much a challenge of organizing one’s thoughts as it is a matter of translating them to a form the computer can understand, and the former is a challenge I have always found rewarding.

                                            Even so, as every programmer inevitably discovers, the gap between one’s thoughts and executable code is never so narrow as we would like. I quickly discovered that a distressingly large amount of my time was being spent on the exactly the sort of repetitive task I had hoped to avoid, mechanically typing out reams of boilerplate in order to carry out what seemed to be incredibly simple tasks.1 It felt strange that we, as programmers, could be one of the lucky disciplines equipped with the means to produce our own tools, yet the tools we work with are so frequently unsatisfactory.

                                            Programming is an astonishingly young discipline. FORTRAN, arguably the first recognizable programming language, is less than seventy years old, and the first stored-program computers are only a decade older. Programmers have been trying to bridge the gap between our conceptualizations of programs and the (often laborious) act of programming for as long as computing has existed. As John Backus said in 1979,

                                            Much of my work has come from being lazy. I didn’t like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs.

                                            In my experience, this sentiment perfectly captures the strange relationship many programming language enthusiasts have with our craft. We enjoy programming so much that we are willing to spend enormous time, thought, and effort working on programming systems precisely to free ourselves from the tortuous burden of writing programs.

                                            One possible means of reconciling this apparent contradiction would be to hypothesize that programmers like me or like Backus enjoy the results of our programs but not the process of writing them. We want to get things done, and we view the computer as a useful but necessary evil that should get out of the way as much and as expediently as possible. However, although I am sure there are many programmers who do match that description, I do not count myself among them, and I would likewise find it extremely difficult to believe that Backus—a man who dedicated his life to designing ever more sophisticated means of expressing computer programs—was someone fundamentally uninterested in programming.

                                            On the contrary, I see a great deal of work on programming languages as a struggle—often in vain—by those of us who love programming so much that we wish to free it of its unfortunate imperfections. We strive to remove the impediments and obstacles we face when translating the beautiful, platonic programs we hold in our heads into the comparatively cold, alien language of silicon calculating machines. There is a widespread mythology that programming languages are primarily abstractions over machine code, but this perspective has been misguided for almost as long as programming languages have existed. Programming languages are instruments of thought, frameworks for precisely specifying an executable idea, and they are designed almost entirely for humans, with computers often providing little more than a loose set of inconvenient restrictions.

                                            To compute is human, to program divine

                                            The earliest computers were developed with a singular purpose in mind: to compute. A program was, essentially, a sequence of instructions containing the computations to be performed. This conceptualization of a computer program as essentially analogous to a cooking recipe or assembly manual has remained curiously persistent, even to the present day.

                                            Almost no modern programs of any complexity look anything like a cooking recipe. Even programs written in the most imperative programming languages under the sun are complex webs of logic specifying dataflow, access policy, and interaction patterns between semi-autonomous, reactive subsystems, many of which come equipped with their own living, beating hearts. At the micro scale, it is true that programs specify behavior as a series of steps to be performed, but even this is a fiction maintained by modern compilers for humans’ convenience. Through a compiler’s eyes, the real programs are whatever is left after the comforting fiction is boiled away to obtain a homogenous pile of SSA and dataflow graphs to be haphazardly reassembled into a patchwork of basic blocks in a language most programmers would not even begin to understand.

                                            Indeed, in stark contrast to the mental model of the programmer as a sort of silicon whisperer, I believe most practicing software engineers are primarily maintaining vast behavioral specifications whose defining sense of rigor comes not from computers per se but from the consistency we demand from their results. In other words, we are trained to be fluent speakers of languages that allow us to articulate a solution to a problem with a level of precision that ensures every legitimate interpretation is and will always be to our satisfaction. Crucially, these specifications do not fully specify how the task is to be carried out. Optimizing compilers are free to make their own choices with whatever leeway we permit them. HTML/CSS user agents may interpret our instructions in whatever way preserves whatever web standards happen to require. Even RDBMS query planners, a foundational technology largely unchanged for the past several decades, are essentially expert systems designed to decide on the fly how best to execute searches and retrievals under ever-changing conditions.

                                            This is not to say that the task of programming is fully removed from the machine that ultimately executes our instructions, nor is it even really a suggestion it ought to be. Performance and debugging mandate a certain familiarity with the underlying system, even if the full complexities of its true inner workings remain carefully hidden from even its expert users. My point is simply that programs are not rote algorithms, and the act of programming is not primarily characterized by algorithmic thinking. Rather, programming is fundamentally a game of domain modeling and system design, with bits of algorithmic logic inserted here and there to glue the whole thing together.

                                            My interest in pushing the frontier of programming language design has always been and continues to be an interest in making the task of writing programs feel closer to the task of designing them. In an ideal world, I would have a language expressive enough to more or less directly translate the computer programs I have in my head (which, for me, more often than not take visual form) into a digitized structure a computer may faithfully execute. It is that task I found myself fixated on at the tender age of fifteen, and it is one I would remain fixated on for the better part of the following decade.

                                            Ten years of programming languages

                                            I turned twenty eight this year. Almost exactly ten years ago, I started my first professional software engineering job. It was a fairly mundane position writing Ruby on Rails and JavaScript, but even then, I spent a great deal of free time pursuing the programming language hobby projects that truly caught my fancy. My first professional experience working with Haskell would come less than a year later, experience that would soon inspire my hobby research project to implement a Haskell-style type system within the Racket macro system. The majority of my career to date has followed more or less directly from that formative time.

                                            After a decade, it seems fair to reflect a little on what I have learned, what I managed to achieve, and maybe more significantly, what I did not. Something one learns very quickly upon beginning any serious work on programming languages is that they are startlingly, unforgivably hard. A vast array of dizzyingly smart people have been working on the problem of program specification for seventy years; there are not many pieces of low hanging fruit left on the tree, and any new gains tend to be very hard won.

                                            Still, even if programming language design is challenging, it would be some consolation if we were able to slowly yet steadily advance the state of the art, gradually picking away at the gap between the tools we have now and the ultimate programming language of the future. Sadly, even that pragmatic vision does not survive contact with reality. In practice, the challenge of programming language design is not one of expanding a well-defined frontier, it is grappling with a neverending list of fundamental tradeoffs between mutually incompatible features.

                                            Subtyping is tantalizingly useful but makes complete type inference incredibly difficult (and in general, provably impossible). Structural typing drastically reduces the burden of assigning a name to every uninteresting intermediate form but more or less precludes taking advantage of Haskell-style typeclasses or Rust-style traits. Dynamic dispatch substantially assists decoupling of software components but can come with a significant performance cost without a JIT. Just-in-time compilation can use runtime information to optimize code in ways that permit more flexible coding patterns, but JIT performance can be unpredictable, and the overhead of an optimizing JIT is substantial. Sophisticated metaprogramming systems can radically cut down on boilerplate and improve program concision and even readability, but they can have substantial runtime or compile-time overhead and tend to thwart automated tooling. The list goes on and on.

                                            The essential, most stubborn problems in programming languages come from unavoidable tensions between conflicting desires and requirements. We want loosely coupled software components that can be easily reused, but we also want the performance benefits of tight coupling and specialization. We want flexible programming languages that do not impose upon our freedom of expression, but we also want the benefits of static program analysis and powerful safety guarantees. We want sophisticated type systems that allow specifying ever more complex invariants, but we also want readable type signatures that won’t regularly end up longer than the code itself.

                                            We cannot build the One True Programming Language because we cannot have everything at once, and we cannot hope to universally decide which tradeoffs are the right ones to make. This naturally makes the thought of a system constructed from a tapestry of many different programming languages attractive, affording the programmer the opportunity to select the tool best suited to the task at hand. Unfortunately, that, too, comes with (often brutal) tradeoffs of its own: the impedance mismatch at language boundaries, the vastly more complex tooling required for a polyglot system, and the increased knowledge burden of maintaining a system built in so many different ways.

                                            Ten years of working on and thinking about programming languages has forced me to come to terms with a humbling truth: I do not know how to build a better programming language. I am personally extremely sympathetic to the idea that the future of programming probably involves more domain-specific languages, and I think the industry as a whole has been (very slowly) trending in that direction for quite some time. But even I acknowledge that the question of how precisely that ought to be done is extraordinarily complicated, and there are no easy answers here.

                                            Even so, it would be far too dismal of me to suggest that the problem is hopeless, nor do I believe the work does not matter. Even if the problem has proven more difficult than teenage me may have hoped, the work is satisfying when it bears fruit. Unfortunately, even after toiling for years overcoming daunting problems, every working programming language enthusiast is forced to confront a much more frustrating enemy: programmers.

                                            The reactionary conservatism of the median programmer

                                            It should come as no surprise that programming language design is largely a social problem. Choice of programming language tends to be driven by strong network effects, and programmers tend to prefer languages that resemble what they already know. Programmers’ general lack of curiosity and outright hostility to learning new things has always been a personal frustration of mine, but I ultimately do understand much of where it comes from: as engineers, an overwhelming part of our job description is managing risk. Exotic technology choices are risky, and it is a responsible impulse to be wary of them.

                                            Still, the history of mainstream programming languages is essentially a story of programmers vocally and emphatically rejecting what eventually proved to be some of the most incredibly successful innovations in the history of the field. Assembly programmers largely laughed at FORTRAN, but just a few decades later, there were nevertheless very few remaining assembly programmers. First-class functions were widely derided as needlessly complicated and confusing until programmers were forced to finally take the time to learn to use them once JavaScript became a load-bearing language by historical accident, and within a decade, they became a required feature for every major programming system. Sophisticated type systems largely retain a perception of overengineered, ivory-tower elitism, but many of the programmers who hold those very opinions have enthusiastically adopted Rust, a language that features a type system so complex that idiomatic Rust code can easily put Haskell programs to shame.

                                            Discussions of programming languages are, by and large, emotionally driven shouting matches based on anecdotes and gut feelings. Surprisingly (or perhaps not), although most working programming language theorists and compiler engineers have a far more informed perspective on this subject (and do at least tend to refrain from petty debate), disparaging remarks directed towards languages not to the speaker’s taste are hardly unheard of even at academic conferences dedicated to the subject. I want to be very clear that I do not think that is somehow indicative of some toxicity within the community—they are by and large a group of truly amazing, wonderful people, and such comments are usually delivered with tongue planted firmly in cheek. All the same, my point is simply that programming languages are an emotional, opinionated subject in a way that seems perhaps inherent to the craft, a topic I explored at some length all the way back in 2019.

                                            I do earnestly believe that the field of software engineering would benefit from more openness to new ideas and willingness to experiment with new technologies. I also think it would be enormously refreshing if programmers did not nearly universally speak with an expert’s confidence on subjects they have only passingly encountered.2 Nevertheless, I do not expect programmers’ tribalism to diminish anytime soon, so the pragmatist in me understands their nature imposes real limitations on what we as a field can meaningfully accomplish.

                                            In my experience, most working programming language researchers have essentially resigned themselves to this fact, and the prevailing wisdom is to almost entirely decouple the value of the work from its impact. Even if an idea is broadly ignored by programmers today, if it’s a good enough idea, it will still make its way into some programming language in some distant tomorrow, as many ideas eventually have. Lexical closures were first applied to programming languages in 1975 but took a full thirty years to be broadly accepted. Algebraic datatypes were first introduced around the same time and have only recently found mainstream acceptance through Rust. Innovations take a long, long time to make the jump from research to practice in this field, and placing one’s sense of professional accomplishment in the fickle hands of mainstream programmers’ whims and preferences is a recipe for personal misery.

                                            I have nothing but respect for this attitude, genuinely. This research community is astonishingly friendly, warm, creative, fun, and accepting, especially given the vitriol these subjects tend to elicit from programmers at large. I feel genuinely honored to have had the opportunity to personally collaborate with many of them, and the collective output of endlessly fascinating ideas and exciting projects one can find each and every year at every major PL conference is consistently inspiring. All the same, as much respect as I have for the people who work tirelessly on these problems for their own sake, I am not sure I can continue to count myself among them. I do find the subject exciting, and I do consider it rewarding in its own way, but these past few years, I have slowly been coming to terms with the fact that I don’t think I find it quite rewarding enough.

                                            Why I write computer programs

                                            I like writing software. It is strange how radical a statement that sometimes feels. So many programmers seem to practically despise what they do for a living, at least as you’d hear them describe it. I have never really been able to empathize with that mindset. I find programming fun, and I derive a unique joy from writing code that decisively solves the problem at hand. It doesn’t have to be a particularly interesting problem, and the solution doesn’t even have to require much more than snapping blocks together, but as long as I can take some pride and satisfaction in the craftsmanship and end result, I am usually pretty happy.

                                            My attraction to libraries, tooling, and compilers has always stemmed from a mixture of personal motivation and a certain satisfaction from improving things for my peers. One of my favorite things about software engineering is taking a problem and solving it in such a way that it largely stays solved. Sure, it may need to change as requirements evolve, but the solution continues to work even after the labor is done. Other tasks in my life do not provide nearly the same sense of accomplishment. When I do a load of laundry, it provides me with clean clothes for a week or two, but ultimately, I know it won’t be long before I’m going to have to do it again. Software engineering is not like that. Working on software—even extremely mundane software—provides a pleasant variety of tasks that can be well and truly completed. I like that sense of progress, and I appreciate the relaxing, almost meditative process of growing and maintaining the metaphorical garden that is a collection of code.

                                            Given all of the above, it is not altogether clear why I would struggle to take satisfaction in my work on programming languages. If anything, working on a compiler seems like the ultimate realization of my aforementioned desires applied to the very thing I, myself, spend my day doing. If there is some annoying or repetitive task, and I can extend my programming system to provide a better way of doing it, that task is something I will never again need to do. What’s more, working on a compiler that thousands of people actually use means this is not just benefiting me, it’s benefiting everyone else as well. It feels good to have that kind of impact, and to know that my effort is essentially multiplied by everyone who happens to use it.

                                            On paper, this ought to make working on a compiler like GHC enormously satisfying. Even if I feel frustrated by mainstream attitudes towards programming languages, Haskell programmers feel differently. Surely, working on something used and appreciated by Haskellers ought to feel very rewarding. But it wasn’t really. There were parts that were nice, yes, and the problems were often interesting. But I didn’t find it rewarding in the way I had hoped, and I’m not sure it even compared favorably to working on bog-standard software. Why?

                                            On the Haskell community

                                            This was the hardest section of this blog post to write. It is easy to write about the things I love and the people I admire. It is perhaps even easier to write about the things I despise and the people I disdain. The Haskell community is neither. I do not dislike it. Several of its members are people I consider very good friends. Nonetheless, I have never managed to fall in any particular love with it, and even if that is no sin on its part, it is a somewhat sad thing to have to confess.

                                            I want to be excruciatingly clear that I do not wish to mount any indictment of the Haskell community or any of the people in it. Many people seem to have a preconception of Haskellers as mean and elitist, and while I do get the sense there have been some historically bad actors that contributed to that perception, my general impression is that those people have been thoroughly expunged. I have never been the target of any memorably rude conduct, and I’ve definitely never felt targeted by any prejudice or discrimination, and if anything I have always felt universally warmly welcomed at every meetup or conference I have attended. I cannot speak for anyone but myself, and my experiences are necessarily limited. I cannot hope to claim with any certainty that no abuse has ever occurred or that it does not continue to occur within any Haskell’s myriad social spaces, most of which I have never participated in. Still, I am happy to be able to say that I have never personally experienced any real unfriendliness, and that is certainly not a factor in my dispassion for the language’s community. If anything, I feel I must extend a very genuine thank you for all the lovely support I have received from Haskellers over the years, and it is that support and enthusiasm that so often kept me motivated for as long as I was.

                                            This is all simply to say that I do not think it is the fault of anyone in particular for my lack of affinity for the Haskell community. Rather, I think perhaps it is just not a culture that I have ever especially related to. That probably does not even say very much, as I’m not sure there are many communities I could say that about. That says much more about me than it does about Haskell or Haskellers.

                                            It is admittedly tempting to say that perhaps I find these communities alienating in part because they lack people I identify with and relate to. After all, it is simply true that the Haskell community is overwhelmingly male, which really has felt lonely at times. It’s also true that the Haskell community tends to attract a lot of people who are mainly interested in Haskell because they find it intellectually interesting, or because they like category theory and enjoy libraries like lens that have explored those ideas, while I am really only interested in Haskell insofar as it is a practical vehicle for writing useful software. Nevertheless, there are other women who write Haskell and there are other people who care very much about writing useful software in Haskell, and that doesn’t seem to have radically altered my relationship to the community, so I think it would be disingenuous of me to claim that either of those flaws are substantially to blame.

                                            If there is one substantive frustration I can express with Haskell, it is where the money comes from. For as long as I have been involved with it, Haskell has predominantly been used and funded by a combination of fintech and cryptocurrency. I don’t really have anything against fintech, though I certainly don’t find it especially exciting, but I do have something against crypto, and those who followed me on Twitter before its untimely demise know I have always been pretty open about that fact. My thoughts on the matter are really not something I want to elaborate on here, but suffice to say it is not especially motivating to work on a language if all of its users are applying it in ways I feel essentially indifferent about at best and actively hostile to at worst. This is obviously not something I have any easy answers to, but it is probably the most significant, concrete factor in me losing my interest in working on GHC, so if you mourn me leaving, at least I can give you one semi-actionable reason.

                                            Make no mistake: GHC is a truly incredible project that often still feels like a compiler from the future, and having the opportunity to work on it as a part of my job was about as technologically stimulating as I could possibly ask for. Even so, during my time at Tweag, I often found myself reflecting on the fact that the users I was working for were, ultimately, Haskell programmers, a class I’m not sure even really contains me. I have often reflected on the fact that, although I have many personal projects, I have never chosen Haskell for any of them. Not once. Whenever I need to write a little code to accomplish some task, it’s always Racket. This blog is in Racket. My personal shell utilities are in Racket. When I want to scrape a website or wrap some library, I do it with Racket. Haskell is a language I have always exclusively written professionally, and although I do very earnestly love many things about it, it is not so precious to me that I feel motivated to contribute to it out of passion alone. If there were more users of Haskell doing inspiring, exciting things with the tools I was working on, I might feel substantially more driven to indirectly contribute to those causes. But Haskell is a niche language used by a select few, and it is hard for me to spend so much of my life working on a technological marvel that practically nobody will ever benefit from.

                                            As a final note on this subject: I do believe very earnestly that the functional programming and programming languages communities would benefit enormously from more demographic diversity. Even compared to software engineering as a whole, the gender ratio is, frankly, almost unbelievably grim. I have always been drawn to interests and hobbies that tend to skew male, and that has never really bothered me, so if I have found myself feeling alienated by the conspicuous absence of other women—to the point that, in the past, I have sometimes considered giving up on Haskell primarily for that reason—I don’t think it is outrageous to say things are pretty dire. A wider set of perspectives could, in theory, inject some refreshing creativity into the dreary ecosystem that is industrial Haskell. Of course, I will admit that I have absolutely no idea how that end could possibly be achieved, and it is a subject far broader and more complicated than I think belongs in this blog post. For now, I will leave things at that, but I hope my voice is sufficiently well regarded that some readers may take my comments to heart.

                                            Reflections on a decade’s work

                                            Before writing this blog post, I essentially pitched it to a group of friends as “a sort of apology”. At times I certainly feel burdened by the weight of all the endeavors I’ve left unfinished, all the work I never carried long enough to bear fruit. Even so, as I write these words, I find myself reflecting on the things I feel I did manage to contribute these past ten years, and it strikes me that the trail I’ve left behind is perhaps not so dismal, after all.

                                            It is comforting to look back on a decade’s accumulations and realize there is enough to take some real pride in. I am proud of my contributions to Racket and Haskell, and of my numerous libraries in each of them. I am proud of my experiments, among them Hackett and eff, and the excellent work from others they have inspired. I am proud of this very blog, from the most impactful things I’ve written to the far more niche. I am proud of my contributions to Programming Languages Stack Exchange, a site I most certainly intend to remain a moderator of and will undoubtedly continue to contribute to. And I am proud of the numerous talks I have presented over the years, all of them labors of love.

                                            I have not always accomplished all the things I set out to achieve. Some have, for some reason or another, gotten the better of me. Many of them, from my math typesetting system to my improvements to GHC’s arrow notation, are things I hope to one day push over the finish line. But for now it is clear my heart is no longer really in them, so I think it is time for me to let them go. Perhaps others will find inspiration in them. Perhaps you will succeed where I did not.

                                            It is a little sad to think about moving on from such a substantial chapter of my life. To date, I have spent almost my entire career in this professional niche, and I suspect I will never fully leave it. It has been a pleasure to work with so many bright, lovely, inspiring people, and perhaps one day I will find myself eager to return once more. But for now, my interests are elsewhere, and despite the bittersweet feelings, there is also a very real excitement in the tantalizing opportunity to do something new.

                                            What next?

                                            It is a little amusing to think that what I find myself most drawn to after a decade of pursuing ever more exotic projects is something entirely mundane: I want to write utterly ordinary software. I want to work on a piece of software used by people to accomplish a task, and I want to take pride and satisfaction in doing it well.

                                            I do not yet really have any idea what that might look like. It has been over six years since I last worked on anything that really feels like it could possibly fit that description, and even then, it was only one part of my job. Is it so strange that after spending fifteen years of my life trying everything I could to get away from spending all day wrangling a big ball of mud in a Java IDE, that experience sounds pretty cozy to return to?

                                            In late 2022, I decided on a whim to reverse engineer and resurrect a 2008 Java browser game called Shattered Plans. While doing so, I had the opportunity to spend an awful lot of time in IntelliJ, and after years of acclimatizing to writing code in fanciful languages with nothing more than a plain text editor, a terminal emulator, and a dream, the experience was almost viscerally thrilling. They say the grass is always greener, but even if that is true, I think I’d like to find it out for myself.

                                            More to the point, my life is just very different now from how it was for most of my twenties. I was single, and it was easy to justify spending almost all of my time thinking about code, whether on the clock or not. It was hardly unheard of for me to spend twelve or even sixteen hours at my desk, just tinkering on something that had captured my attention. It was fun, and I’m glad I did it, but I am also very ready to work a simple, regular job I don’t feel the need to permanently devote a large portion of my soul to. It isn’t very romantic, but then, I have other things for that.

                                            If you think you have a position that meets those (admittedly extremely broad) criteria, and you think you might like to hire me, by all means: send me an email. The worst I can do is not reply. And who knows? With the burden of my own expectations behind me, perhaps I’ll even blow the dust off this old blog of mine and start writing things again. As with my job, whatever I might choose to write next I cannot say. Either way, it’s a little exciting to be able to treat myself to a blank slate.

                                            I could probably spend twenty thousand words writing in circles about all my thoughts and feelings on this subject. However, to be honest, I don’t especially want to. As this blog post has hopefully communicated, I would like to be done, and so this will have to be enough. Allow me to personally thank all of you who have indulged me by reading my disorganized ramblings to the end.

                                            It’s been a long time. Here’s to another ten years.

                                            1. Readers may find it clarifying to note that a great deal of my early programming experience was writing (then era-appropriate) Java 6.

                                            2. I have often wondered if other engineering disciplines have anything to rival the collective overconfidence of the average Hacker News commenter. This is an earnest curiosity, not merely a rhetorical flourish; I do sometimes wonder what it is about my field of choice that engenders such disregard for the value of expertise.

                                            \ No newline at end of file diff --git a/blog/build.rkt b/blog/build.rkt deleted file mode 100644 index e58c44c..0000000 --- a/blog/build.rkt +++ /dev/null @@ -1,283 +0,0 @@ -#lang racket/base - -(require (for-syntax racket/base) - racket/class - racket/date - racket/file - racket/format - racket/lazy-require - racket/list - racket/match - racket/path - racket/promise - racket/runtime-path - racket/sequence - racket/serialize - scribble/core - scribble/xref - setup/xref - syntax/parse/define - threading - (only-in xml write-xexpr) - - "build/metadata.rkt" - "build/render/scribble.rkt" - "build/render/feed.rkt" - "build/render/page.rkt" - "lang/metadata.rkt" - "markdown.rkt" - "paths.rkt") - -(define num-posts-per-page 10) - -(struct post-dep (src-path main-part-promise) #:transparent) - -(define (post-dep-main-part dep) - (force (post-dep-main-part-promise dep))) - -(define (post-dep-info-path dep) - (~> (file-name-from-path (post-dep-src-path dep)) - (path-replace-extension #".info") - (build-path build-dir _))) - -(define (post-dep-xref-path dep) - (~> (file-name-from-path (post-dep-src-path dep)) - (path-replace-extension #".sxref") - (build-path build-dir _))) - -;; Functions like `other-doc` assume that each top-level document has the tag -;; '(part "top") on its top-level section. This is only ensured by -;; setup/scribble, which we are not using, so we have to add it manually. -(define (ensure-top-tag main-part) - (if (member '(part "top") (part-tags main-part)) - main-part - (struct-copy part main-part - [tags (cons '(part "top") (part-tags main-part))]))) - -(define (set-blog-tag-prefix main-part path-str) - (struct-copy part main-part [tag-prefix (blog-post-path->tag-prefix path-str)])) - -(define (markdown-post file-name) - (define path (build-path posts-dir file-name)) - (post-dep path - (delay (~> (call-with-input-file* path parse-markdown-post) - ensure-top-tag - (set-blog-tag-prefix file-name))))) - -(define (make-scribble-post-dep file-name) - (define path (build-path posts-dir file-name)) - (post-dep path - (delay (~> (dynamic-require path 'doc) - ensure-top-tag - (set-blog-tag-prefix file-name))))) - -(define-syntax-parser scribble-post - [(_ mod-path:string) - ; add compile-time dependency - (syntax-local-lift-require - #`(for-label (only #,(string-append "posts/" (syntax-e #'mod-path)))) - #'#f) - #'(make-scribble-post-dep 'mod-path)]) - -(define all-post-deps - (list (markdown-post "2015-07-18-automatically-deploying-a-frog-powered-blog-to-github-pages.md") - (markdown-post "2015-08-22-deploying-racket-applications-on-heroku.md") - (markdown-post "2015-08-30-managing-application-configuration-with-envy.md") - (markdown-post "2015-09-23-canonical-factories-for-testing-with-factory-girl-api.md") - (markdown-post "2015-11-06-functionally-updating-record-types-in-elm.md") - (markdown-post "2015-12-21-adts-in-typed-racket-with-macros.md") - (markdown-post "2016-02-18-simple-safe-multimethods-in-racket.md") - (markdown-post "2016-06-03-four-months-with-haskell.md") - (markdown-post "2016-08-11-climbing-the-infinite-ladder-of-abstraction.md") - (markdown-post "2016-08-24-understanding-the-npm-dependency-model.md") - (markdown-post "2016-10-01-using-types-to-unit-test-in-haskell.md") - (markdown-post "2017-01-02-rascal-a-haskell-with-more-parentheses.md") - (markdown-post "2017-01-05-rascal-is-now-hackett-plus-some-answers-to-questions.md") - (markdown-post "2017-04-28-lifts-for-free-making-mtl-typeclasses-derivable.md") - (markdown-post "2017-05-27-realizing-hackett-a-metaprogrammable-haskell.md") - (markdown-post "2017-06-29-unit-testing-effectful-haskell-with-monad-mock.md") - (markdown-post "2017-08-12-user-programmable-infix-operators-in-racket.md") - (markdown-post "2017-08-28-hackett-progress-report-documentation-quality-of-life-and-snake.md") - (markdown-post "2017-10-27-a-space-of-their-own-adding-a-type-namespace-to-hackett.md") - (markdown-post "2018-02-10-an-opinionated-guide-to-haskell-in-2018.md") - (markdown-post "2018-04-15-reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket.md") - (markdown-post "2018-09-13-custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions.md") - (markdown-post "2018-10-06-macroexpand-anywhere-with-local-apply-transformer.md") - (markdown-post "2019-04-21-defeating-racket-s-separate-compilation-guarantee.md") - (markdown-post "2019-09-07-demystifying-monadbasecontrol.md") - (markdown-post "2019-10-19-empathy-and-subjective-experience-in-programming-languages.md") - (markdown-post "2019-11-05-parse-don-t-validate.md") - (markdown-post "2020-01-19-no-dynamic-type-systems-are-not-inherently-more-open.md") - (markdown-post "2020-08-13-types-as-axioms-or-playing-god-with-static-types.md") - (markdown-post "2020-11-01-names-are-not-type-safety.md") - (markdown-post "2021-03-25-an-introduction-to-typeclass-metaprogramming.md") - (markdown-post "2025-05-29-a-break-from-programming-languages.md"))) - -(define (timestamp-string) - (define (pad n) (~r n #:min-width 2 #:pad-string "0")) - (define now (current-date)) - (~a "[" (pad (date-hour now)) - ":" (pad (date-minute now)) - ":" (pad (date-second now)) - "]")) - -(define (write-html xexpr out) - (display "" out) - (write-xexpr xexpr out)) - -(define (write-xml xexpr out) - (display "" out) - (write-xexpr xexpr out)) - -(define (render-scribble render% main-part out-path - #:dest-dir [dest-dir (path-only out-path)] - #:xrefs-in [xref-in-paths '()] - #:xref-out [xref-out-path #f]) - (define main-parts (list main-part)) - (define out-paths (list out-path)) - (define renderer (new render% [dest-dir dest-dir])) - - (define traverse-info (send renderer traverse main-parts out-paths)) - (define collect-info (send renderer collect main-parts out-paths traverse-info)) - - (for ([path (in-list xref-in-paths)]) - (define xref-in (call-with-input-file* path read)) - (send renderer deserialize-info xref-in collect-info)) - (xref-transfer-info renderer collect-info (load-collections-xref)) - - (define resolve-info (send renderer resolve main-parts out-paths collect-info)) - (match-define (list render-result) - (send renderer render main-parts out-paths resolve-info)) - - (when xref-out-path - (define out-info (send renderer serialize-info resolve-info)) - (call-with-output-file* #:exists 'truncate/replace - xref-out-path - (λ~>> (write out-info)))) - - (define undefined-tags (send renderer get-undefined resolve-info)) - (unless (empty? undefined-tags) - (eprintf "Warning: some cross references may be broken due to undefined tags:\n") - (for ([tag (in-list undefined-tags)]) - (eprintf " ~s\n" tag))) - - render-result) - -(define (build-post-body dep #:xrefs-in xref-in-paths) - (define src-mod-time (file-or-directory-modify-seconds (post-dep-src-path dep) #f (λ () #f))) - (define info-mod-time (file-or-directory-modify-seconds (post-dep-info-path dep) #f (λ () #f))) - (cond - ; Note: this check doesn’t handle the case where we ought to rebuild because - ; the xrefs-in changed. This is because we don’t currently implement - ; dependency tracking for blog posts, so we’d have to pessimistically - ; rebuild *all* newer blog posts whenever an older one changed. This seems - ; okay for now, since older blog posts don’t change very often, and even - ; when they do, they usually don’t change in ways that would invalidate - ; external references. - [(and src-mod-time info-mod-time (> info-mod-time src-mod-time)) - (deserialize (call-with-input-file* (post-dep-info-path dep) read))] - [else - (eprintf "~a running /~a\n" (timestamp-string) (find-relative-path posts-dir (post-dep-src-path dep))) - (render-scribble blog-post-render% - (post-dep-main-part dep) - (post-dep-info-path dep) - #:xrefs-in xref-in-paths - #:xref-out (post-dep-xref-path dep))])) - -(define (build-post-page info #:older older-info #:newer newer-info) - (define site-path (rendered-post-path info #:file? #t)) - (define out-path (reroot-path site-path output-dir)) - (eprintf "~a rendering ~a\n" (timestamp-string) site-path) - (make-parent-directory* out-path) - (call-with-output-file* - #:exists 'truncate/replace - out-path - (λ~>> (write-html (post-page info #:older older-info #:newer newer-info))))) - -(define (build-index-page total-pages page-number posts #:tag [tag #f]) - (define site-path (index-path page-number #:tag tag #:file? #t)) - (define out-path (reroot-path site-path output-dir)) - (eprintf "~a rendering ~a\n" (timestamp-string) site-path) - (make-parent-directory* out-path) - (call-with-output-file* #:exists 'truncate/replace - out-path - (λ~>> (write-html (index-page total-pages page-number posts #:tag tag))))) - -(define (build-feeds posts #:tag [tag #f]) - (build-feed 'atom posts #:tag tag) - (build-feed 'rss posts #:tag tag)) - -(define (build-feed type posts #:tag tag) - (define site-path (feed-path type #:tag tag)) - (define out-path (reroot-path site-path output-dir)) - (eprintf "~a rendering ~a\n" (timestamp-string) site-path) - (make-parent-directory* out-path) - (call-with-output-file* #:exists 'truncate/replace - out-path - (λ~>> (write-xml (feed type posts #:tag tag))))) - -(define (build-about-me) - (define site-path "/about.html") - (eprintf "~a rendering ~a\n" (timestamp-string) site-path) - (local-require (only-in "posts/about-me.scrbl" [doc main-part])) - (render-scribble (blog-standalone-page-render% standalone-page) - main-part - (reroot-path site-path output-dir) - #:xrefs-in (map post-dep-xref-path all-post-deps))) - -(define (build-sitemap posts) - (define site-path "/sitemap.txt") - (eprintf "~a rendering ~a\n" (timestamp-string) site-path) - (call-with-output-file* - #:exists 'truncate/replace - (reroot-path site-path output-dir) - (λ (out) - (displayln (full-url "/about.html") out) - (for ([post (in-list posts)]) - (displayln (full-url (rendered-post-path post)) out))))) - -(define (build-all) - (make-directory* build-dir) - (make-directory* output-dir) - - (define all-posts - (for/fold ([rendered-results '()] - [prev-xrefs '()] - #:result (reverse rendered-results)) - ([dep (in-list all-post-deps)]) - ; Currently, we only pass the xrefs from previous blog posts, which means - ; circular references between blog posts is impossible. Supporting that - ; would require a more elaborate strategy, and it isn’t necessary at the - ; moment, so we stick to the simple thing for now. - (values (cons (build-post-body dep #:xrefs-in prev-xrefs) rendered-results) - (cons (post-dep-xref-path dep) prev-xrefs)))) - - (for ([post (in-list all-posts)] - [older-post (in-list (cons #f all-posts))] - [newer-post (in-list (append (rest all-posts) (list #f)))]) - (build-post-page post #:older older-post #:newer newer-post)) - - (define total-pages (ceiling (/ (length all-post-deps) num-posts-per-page))) - (for ([posts (in-slice num-posts-per-page (reverse all-posts))] - [number (in-naturals 1)]) - (build-index-page total-pages number posts)) - (build-feeds (reverse all-posts)) - - (define tagged-posts - (for*/fold ([tagged-deps+infos (hash)]) - ([post (in-list all-posts)] - [tag (in-list (rendered-post-tags post))]) - (hash-update tagged-deps+infos tag (λ~>> (cons post)) '()))) - - (for ([(tag posts) (in-immutable-hash tagged-posts)]) - (define total-pages (ceiling (/ (length posts) num-posts-per-page))) - (for ([posts (in-slice num-posts-per-page posts)] - [page-number (in-naturals 1)]) - (build-index-page total-pages page-number posts #:tag tag)) - (build-feeds posts #:tag tag)) - - (build-about-me) - (build-sitemap all-posts)) - -(module+ main - (build-all)) diff --git a/blog/build/metadata.rkt b/blog/build/metadata.rkt deleted file mode 100644 index b983d12..0000000 --- a/blog/build/metadata.rkt +++ /dev/null @@ -1,24 +0,0 @@ -#lang racket/base - -(require racket/contract - racket/serialize - (only-in xml xexpr/c) - - "../paths.rkt" - "../lang/metadata.rkt") - -(provide (struct-out rendered-post) - (contract-out - [rendered-post-path (->* [rendered-post?] [#:file? any/c] site-path?)])) - -(serializable-struct rendered-post (title-str title date tags body) #:transparent - #:guard (struct-guard/c string? - (listof xexpr/c) - post-date? - (listof string?) - (listof xexpr/c))) - -(define (rendered-post-path post #:file? [file? #f]) - (post-path (rendered-post-date post) - (rendered-post-title-str post) - #:file? file?)) diff --git a/blog/build/render/feed.rkt b/blog/build/render/feed.rkt deleted file mode 100644 index ab72066..0000000 --- a/blog/build/render/feed.rkt +++ /dev/null @@ -1,66 +0,0 @@ -#lang racket/base - -(require racket/contract - racket/format - racket/list - racket/match - (only-in xml xexpr/c xexpr->string) - - "../../lang/metadata.rkt" - "../../paths.rkt" - "../metadata.rkt" - (only-in "page.rkt" index-page-title)) - -(provide (contract-out - [feed (->* [(or/c 'atom 'rss) (listof rendered-post?)] - [#:tag (or/c string? #f)] - xexpr/c)])) - -(define (feed type posts #:tag [tag #f]) - (match type - ['atom - `(feed ([xmlns "http://www.w3.org/2005/Atom"] [xml:lang "en"]) - (title ,(index-page-title #:tag tag)) - (link ([rel "self"] [href ,(full-url (feed-path 'atom #:tag tag))])) - (link ([rel "alternate"] [href ,(full-url (index-path #:tag tag))])) - (updated ,(post-date->rfc-3339-datetime (rendered-post-date (first posts)))) - ,@(for/list ([post (in-list posts)]) - (match-define (rendered-post title-str _ date tags body) post) - `(entry - (title ,title-str) - (link ([rel "alternate"] [href ,(full-url (rendered-post-path post))])) - (published ,(post-date->rfc-3339-datetime date)) - (updated ,(post-date->rfc-3339-datetime date)) - (author (name "Alexis King")) - (content ([type "html"]) ,(xexpr->string `(article ,@body))))))] - - ['rss - (define updated (post-date->rfc-822-date (rendered-post-date (first posts)))) - `(rss ([version "2.0"]) - (channel - (title ,(index-page-title #:tag tag)) - (description ,(index-page-title #:tag tag)) - (link ,(full-url (index-path #:tag tag))) - (pubDate ,updated) - (lastBuildDate ,updated) - (ttl "60") - ,@(for/list ([post (in-list posts)]) - (match-define (rendered-post title-str _ date tags body) post) - `(item - (title ,title-str) - (link ,(full-url (rendered-post-path post))) - (guid ([isPermaLink "true"]) ,(full-url (rendered-post-path post))) - (pubDate ,(post-date->rfc-822-date date)) - (description ,(xexpr->string `(article ,@body)))))))])) - -(define (post-date->rfc-3339-datetime date) - (~a (post-date->string date) "T00:00:00Z")) - -;; The RSS spec demands that dates be in this somewhat odd RFC 822 format, and -;; this appears to be important for at least some actual RSS implementations; -;; see lexi-lambda.github.io#10. -(define (post-date->rfc-822-date date) - (~a (~r (post-date-day date) #:min-width 2 #:pad-string "0") - " " (vector-ref #("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec") - (sub1 (post-date-month date))) - " " (post-date-year date))) diff --git a/blog/build/render/highlight/pygments-server.py b/blog/build/render/highlight/pygments-server.py deleted file mode 100644 index a28be7f..0000000 --- a/blog/build/render/highlight/pygments-server.py +++ /dev/null @@ -1,14 +0,0 @@ -import json -import sys -from pygments import highlight -from pygments.lexers import get_lexer_by_name -from pygments.formatters import HtmlFormatter - -formatter = HtmlFormatter(nowrap=True) - -for request_line in sys.stdin: - request = json.loads(request_line) - lexer = get_lexer_by_name(request["language"], encoding="utf-8") - response = highlight(request["source"], lexer, formatter).rstrip() - json.dump(response, sys.stdout, ensure_ascii=False) - sys.stdout.flush() diff --git a/blog/build/render/highlight/pygments.rkt b/blog/build/render/highlight/pygments.rkt deleted file mode 100644 index 49fbd9e..0000000 --- a/blog/build/render/highlight/pygments.rkt +++ /dev/null @@ -1,114 +0,0 @@ -#lang racket/base - -(require json - racket/contract - racket/exn - racket/match - racket/port - racket/runtime-path - racket/system - threading - xml) - -(provide pygments-server? - open-pygments-server - (contract-out [pygments-server-closed? (-> pygments-server? boolean?)] - [current-pygments-server (parameter/c (or/c pygments-server? #f))] - [close-pygments-server (-> pygments-server? void?)] - [call-with-pygments-server (-> (-> pygments-server? any) any)] - [call-with-current-pygments-server (-> (-> any) any)] - [pygmentize (->* [string? #:language string?] [#:server pygments-server?] xexpr/c)])) - -(define-runtime-path pygments-server.py "pygments-server.py") - -(struct pygments-server (pid stdout stdin stderr interact lock cust) - #:property prop:object-name (struct-field-index pid) - #:property prop:custom-write - (λ (self out mode) (fprintf out "#" (pygments-server-pid self)))) - -(define (pygments-server-closed? server) - (custodian-shut-down? (pygments-server-cust server))) - -(define current-pygments-server (make-parameter #f)) - -(define (open-pygments-server) - (define python3 (find-executable-path "python3")) - (unless python3 - (raise-arguments-error 'make-pygmentize "python3 is not in PATH")) - - (define cust (make-custodian)) - (match-define (list stdout stdin pid stderr interact) - (parameterize* ([current-subprocess-custodian-mode 'kill] - [current-custodian cust]) - (process* python3 pygments-server.py))) - (define server (pygments-server pid stdout stdin stderr interact (make-semaphore 1) cust)) - server) - -(define (close-pygments-server server) - (custodian-shutdown-all (pygments-server-cust server))) - -(define (call-with-pygments-server proc) - (call-with-continuation-barrier - (λ () - (define break-paramz (current-break-parameterization)) - (parameterize-break #f - (define server (open-pygments-server)) - (dynamic-wind - (λ () #f) - (λ () (call-with-break-parameterization break-paramz - (λ () (proc server)))) - (λ () (close-pygments-server server))))))) - -(define (call-with-current-pygments-server thunk) - (call-with-pygments-server - (λ (server) - (parameterize ([current-pygments-server server]) - (thunk))))) - -(define (pygmentize source - #:language language - #:server [server (or (current-pygments-server) - (raise-arguments-error 'pygmentize "current-pygments-server is not set"))]) - (define (raise-closed-error) - (raise-argument-error 'pygmentize "(not/c pygments-server-closed?)" pygments-server)) - (when (pygments-server-closed? server) - (raise-closed-error)) - - (call-with-semaphore - (pygments-server-lock server) - (λ () - (unless (eq? ((pygments-server-interact server) 'status) 'running) - (define stderr (port->string (pygments-server-stderr server))) - (close-pygments-server server) - (raise-arguments-error 'pygmentize "pygments server is no longer running" - "stderr..." (unquoted-printing-string stderr) - "exit code" ((pygments-server-interact server) 'exit-code))) - - (with-handlers ([(λ (exn) (and (exn:fail? exn) (pygments-server-closed? server))) - (λ (_) (raise-closed-error))]) - (call-in-nested-thread - (λ () - (write-json (hasheq 'language language 'source source) - (pygments-server-stdin server)) - (newline (pygments-server-stdin server)) - (flush-output (pygments-server-stdin server))) - (pygments-server-cust server))) - - (with-handlers* ([exn:fail? - (λ (exn) - ((pygments-server-interact server) 'kill) - ((pygments-server-interact server) 'wait) - (define stderr (port->string (pygments-server-stderr server))) - (close-pygments-server server) - (raise-arguments-error 'pygmentize "error reading from pygments server" - "read error..." (exn->string exn) - "stderr..." (unquoted-printing-string stderr) - "exit code" ((pygments-server-interact server) 'exit-code)))] - [(λ (exn) (and (exn:fail? exn) (pygments-server-closed? server))) - (λ (_) (raise-closed-error))]) - (call-in-nested-thread - (λ () - (~> (read-json (pygments-server-stdout server)) - (string-append "" _ "") - string->xexpr)) - (pygments-server-cust server)))))) diff --git a/blog/build/render/page.rkt b/blog/build/render/page.rkt deleted file mode 100644 index 6786ad1..0000000 --- a/blog/build/render/page.rkt +++ /dev/null @@ -1,140 +0,0 @@ -#lang racket/base - -(require racket/contract - racket/date - racket/format - racket/list - racket/match - threading - (only-in xml xexpr/c) - - "../../lang/metadata.rkt" - "../../paths.rkt" - "../metadata.rkt" - "util.rkt") - -(provide (contract-out - [page (->* [#:title string? #:body xexpr/c] [#:tag (or/c string? #f)] xexpr/c)] - [standalone-page (-> #:title string? #:body (listof xexpr/c) xexpr/c)] - [post-page (-> rendered-post? - #:older (or/c rendered-post? #f) - #:newer (or/c rendered-post? #f) xexpr/c)] - [index-page-title (->* [] [#:tag (or/c string? #f)] string?)] - [index-page (->i ([total-pages exact-positive-integer?] - [page-number (total-pages) (and/c exact-positive-integer? (<=/c total-pages))] - [posts (listof rendered-post?)]) - (#:tag [tag (or/c string? #f)]) - [result xexpr/c])])) - -(define (page #:title title #:body body #:tag [tag #f]) - `(html - (head - (meta ([charset "utf-8"])) - (meta ([name "viewport"] [content "width=device-width, initial-scale=1"])) - (title ,title) - ,(stylesheet (~a "https://fonts.googleapis.com/css?family" - "=Merriweather+Sans:400,300,300italic,400italic,700,700italic,800,800italic" - "|Merriweather:400,300,300italic,400italic,700,700italic,900,900italic" - "|Fira+Code:300,400,500,600,700")) - ,(stylesheet "/css/application.min.css") - ,(stylesheet "/css/pygments.min.css") - (link ([rel "alternate"] [type "application/atom+xml"] [title "Atom Feed"] [href ,(feed-path 'atom #:tag tag)])) - (link ([rel "alternate"] [type "application/rss+xml"] [title "RSS Feed"] [href ,(feed-path 'rss #:tag tag)])) - (script ([async ""] [src "https://www.googletagmanager.com/gtag/js?id=UA-65250372-1"])) - (script ,(~a "window.dataLayer = window.dataLayer || [];" - "function gtag(){dataLayer.push(arguments);};" - "gtag('js', new Date());" - "gtag('config', 'UA-65250372-1');")) - (body - (header - (nav ([role "navigation"] [class "navigation-bar"]) - (ul ([class "navigation-items left"]) - (li ([class "blog-title-header"]) - (a ([href "/"]) "Alexis King"))) - (ul ([class "navigation-items center"])) - (ul ([class "navigation-items right"]) - (li (a ([href "/"]) "Home")) - (li (a ([href "/about.html"]) "About Me"))))) - (section ([role "main"]) ,body) - (footer - (div ([class "copyright-notice"]) "© " ,(~a (date-year (current-date))) ", Alexis King") - (div "Built with " - (a ([href "https://docs.racket-lang.org/scribble/index.html"]) (strong "Scribble")) - ", the Racket document preparation system.") - (div "Feeds are available via " (a ([href ,(feed-path 'atom)]) "Atom") - " or " (a ([href ,(feed-path 'rss)]) "RSS") ".")))))) - -(define (standalone-page #:title title #:body body) - (page #:title title - #:body `(div ([class "content"]) - (article ([class "main"]) ,@body)))) - -(define (post-page info #:older older-info #:newer newer-info) - (match-define (rendered-post title-str title date tags body) info) - (page #:title title-str - #:body `(div ([class "content"]) - (article ([class "main"]) - ,(build-post-header title date tags) - ,@body - (ul ([class "post-navigation"]) - (li ([class "previous"]) - ,@(when/list newer-info - `(a ([href ,(rendered-post-path newer-info)]) - "←" nbsp (span ([class "post-title"]) ,@(rendered-post-title newer-info))))) - (li ([class "next"]) - ,@(when/list older-info - `(a ([href ,(rendered-post-path older-info)]) - (span ([class "post-title"]) ,@(rendered-post-title older-info)) nbsp "→")))))))) - -(define (index-page-title #:tag [tag #f]) - (~a (if tag (~a "Posts tagged ‘" tag "’ | ") "") - "Alexis King’s Blog")) - -(define (index-page total-pages page-number posts #:tag [tag #f]) - (page #:title (index-page-title #:tag tag) - #:tag tag - #:body `(div ([class "content"]) - ,@(when/list tag - `(h1 ([class "tag-page-header"]) - "Posts tagged " (em ,tag))) - ,@(build-post-index (λ~>> (index-path #:tag tag)) - total-pages page-number posts)))) - -(define (build-post-header title date tags) - (define date-str (post-date->string date)) - `(header - (h1 ([class "title"]) ,@title) - (div ([class "date-and-tags"]) - (time ([datetime ,date-str]) ,date-str) - " " (span ([style "margin: 0 5px"]) "⦿") " " - ,@(~> (for/list ([tag (in-list tags)]) - `(a ([href ,(index-path #:tag tag)]) ,tag)) - (add-between ", "))))) - -(define (build-post-index page-path total-pages page-number posts) - `[,@(for/list ([post (in-list posts)]) - (build-post-index-entry post)) - (ul ([class "pagination"]) - ,(if (= page-number 1) - '(li ([class "disabled"]) "←") - `(li (a ([href ,(page-path (sub1 page-number))]) "←"))) - ,@(for/list ([i (in-range 1 (add1 total-pages))]) - `(li ,(if (= i page-number) - '([class "pagination-number active"]) - '([class "pagination-number"])) - (a ([href ,(page-path i)]) ,(~a i)))) - ,(if (= page-number total-pages) - '(li ([class "disabled"]) "→") - `(li (a ([href ,(page-path (add1 page-number))]) "→"))))]) - -(define (build-post-index-entry post) - (match-define (rendered-post _ title date tags body) post) - (define path (rendered-post-path post)) - `(article ([class "inline"]) - ,(build-post-header `[(a ([href ,path]) ,@title)] date tags) - ; only render up to the start of the first section - ,@(takef body (match-lambda [(cons 'h2 _) #f] [_ #t])) - (p (a ([href ,path]) (span ([class "read-more-text"]) "Read more") " →")))) - -(define (stylesheet href) - `(link ([rel "stylesheet"] [type "text/css"] [href ,href]))) diff --git a/blog/build/render/scribble.rkt b/blog/build/render/scribble.rkt deleted file mode 100644 index 100457c..0000000 --- a/blog/build/render/scribble.rkt +++ /dev/null @@ -1,715 +0,0 @@ -#lang racket/base - -(require (for-syntax racket/base) - racket/class - racket/contract - racket/format - racket/hash - racket/list - racket/match - racket/path - racket/serialize - racket/string - - scribble/base-render - scribble/core - (except-in scribble/html-properties attributes attributes-assoc) - (prefix-in scribble: scribble/html-properties) - (prefix-in scribble: scribble/html-render) - (only-in scribble/private/render-utils part-style?) - - net/uri-codec - net/url - setup/dirs - syntax/parse/define - threading - (only-in xml write-xexpr xexpr/c) - - "../../lang/base.rkt" - "../../lang/metadata.rkt" - "../../paths.rkt" - "../metadata.rkt" - "highlight/pygments.rkt" - "util.rkt") - -(provide base-render% - blog-post-render% - (contract-out - [blog-standalone-page-render% - (-> (-> #:title string? #:body (listof xexpr/c) xexpr/c) - (implementation?/c render<%>))]) - - blog-render-mixin - pygments-render-mixin) - -;; ----------------------------------------------------------------------------- - -(struct attributes (assoc styles) #:transparent - #:name make-attributes - #:constructor-name make-attributes) - -(define empty-attributes (make-attributes (hasheq) (hasheq))) - -(define (attributes-empty? attrs) - (and (hash-empty? (attributes-assoc attrs)) - (hash-empty? (attributes-styles attrs)))) - -(define (attributes #:styles [styles (hasheq)] . pairs) - (if (and (empty? pairs) (hash-empty? styles)) - empty-attributes - (make-attributes (apply hasheq pairs) styles))) - -(define (list->attributes [pairs '()] #:styles [styles '()]) - (if (and (empty? pairs) (empty? styles)) - empty-attributes - (make-attributes (make-immutable-hasheq pairs) (make-immutable-hasheq styles)))) - -(define (attributes-union #:allow-override? [allow-override? #f] attrs . attrss) - (define (multiple-values-error what k a b) - (error 'attributes-union - (~a "multiple values for " what "\n" - " " what ": ~e\n" - " values:\n" - " ~e\n" - " ~e") - k a b)) - - (for/fold ([attrs attrs]) - ([new-attrs (in-list attrss)]) - (make-attributes - (hash-union (attributes-assoc attrs) - (attributes-assoc new-attrs) - #:combine/key - (λ (k a b) - (match k - ['class (string-append a " " b)] - [_ (if allow-override? - b - (multiple-values-error "attribute" k a b))]))) - (hash-union (attributes-styles attrs) - (attributes-styles new-attrs) - #:combine/key - (λ (k a b) (multiple-values-error "CSS property" k a b)))))) - -(define (attributes->list attrs) - (define all-attrs - (cond - [(hash-empty? (attributes-styles attrs)) - (attributes-assoc attrs)] - [(hash-ref (attributes-assoc attrs) 'style #f) - => (λ (existing-styles) - (raise-arguments-error 'render (~a "conflicting value for ‘style’ attribute;\n" - " values were given via both ‘attributes’ and ‘css-styles’ properties") - "attribute value" existing-styles - "css-styles properties" (attributes-styles attrs)))] - [else - (define style-strs (for/list ([(k v) (in-hash (attributes-styles attrs))]) - (~a k ":" v))) - (hash-set (attributes-assoc attrs) 'style (string-join style-strs ";"))])) - (for/list ([(k v) (in-immutable-hash all-attrs)]) - (list k v))) - -(define (style->tag-name+attributes s ri) - (for/fold ([tag-name #f] - [attrs (if (string? (style-name s)) - (attributes 'class (style-name s)) - (attributes))]) - ([prop (in-list (style-properties s))]) - (match prop - ['div - (values 'div attrs)] - [(alt-tag name) - (values (string->symbol name) attrs)] - [(scribble:attributes assoc) - (values tag-name (attributes-union attrs (list->attributes assoc)))] - [(css-styles assoc) - (values tag-name (attributes-union attrs (list->attributes #:styles assoc)))] - [(link-target tag) - (values tag-name (attributes-union attrs - (attributes 'id (tag->anchor-name (resolve-tag tag ri)))))] - [_ - (values tag-name attrs)]))) - -(define (resolve-tag base-tag ri) - (add-current-tag-prefix (tag-key base-tag ri))) - -;; Taken from `scribble/base-render` to match what the base renderer does. -(define (extend-tag-prefix d fresh?) - (cond - [fresh? null] - [(part-tag-prefix d) - (cons (part-tag-prefix d) (current-tag-prefixes))] - [else (current-tag-prefixes)])) - -(define (part-render-style d) - (or (findf link-render-style? (style-properties (part-style d))) - (current-link-render-style))) - -(define tag->local-redirect-query-string - (let () - (define racket-renderer (new (scribble:render-mixin render%) - [dest-dir (find-system-path 'temp-dir)])) - (with-method ([tag->query-string {racket-renderer tag->query-string}]) - (λ (tag) (tag->query-string tag))))) - -; values associated with tags during the collect pass -(serializable-struct blog-page (title path) #:transparent - #:guard (struct-guard/c content? site-path?)) -(serializable-struct blog-page-anchor (title path anchor) #:transparent - #:guard (struct-guard/c (or/c content? #f) site-path? string?)) - -(define header-depth->html-tag - (match-lambda - [0 'h1] - [1 'h2] - [2 'h3] - [3 'h4] - [4 'h5] - [_ 'h6])) - -(define current-output-file (make-parameter #f)) -(define current-top-part (make-parameter #f)) - -;; ----------------------------------------------------------------------------- - -;; Assists with the “overment + augride” trick used to hijack method dispatch -;; without otherwise disrupting downstream subclasses, as discussed here: -;; https://groups.google.com/g/racket-users/c/gM6DB7hY8nU/m/KaRpol06AQAJ -(define-syntax-parser class/hijack - [(_ supercls:expr - #:hijack-methods [hijack-id:id ...] - body-form ...) - #:with [super-hijack-id ...] (generate-temporaries (attribute hijack-id)) - #`(let () - (define-local-member-name super-hijack-id ...) - (class #,(syntax/loc this-syntax - (class supercls - (define/public-final (super-hijack-id . args) - (super hijack-id . args)) - ... - body-form ...)) - (inherit super-hijack-id ...) - (define/augride (hijack-id . args) - (super-hijack-id . args)) - ... - (super-new)))]) - -(define-simple-macro (inner/hijack method:id . args) - (inner (error 'inner/hijack "the impossible happened (maybe ~a wasn’t hijacked?)" 'method) - method . args)) - -;; Like `render%` from scribble/base-render, but with some methods cleaned up or -;; replaced for greater consistency. In particular: -;; -;; * The `current-output-file` and `current-top-part` parameters are set -;; during the collect and render passes. -;; -;; * `render-nested-flow` calls `render-flow` instead of calling `render-block` -;; directly. As a side-effect of this change, `render-nested-flow` returns a -;; flat list (of rendered blocks) rather than a list of lists. -;; -;; Additionally, `base-render%` handles appropriately traversing, collecting, -;; and resolving content in any `external-title` style properties on parts, -;; though it does not use the content for anything. -(define base-render% - (class/hijack render% - #:hijack-methods [render-one] - (inherit traverse-content - collect-content collect-part - fresh-tag-resolve-context? resolve-content resolve-flow - render-flow) - - (define/override (traverse-part d fp) - (~>> (cond - [(findf external-title? (style-properties (part-style d))) - => (λ (ext-title) - (traverse-content (external-title-content ext-title) fp))] - [else fp]) - (super traverse-part d))) - - (define/override (start-collect ds fns ci) - (for-each (lambda (d fn) - (parameterize ([current-output-file fn] - [current-top-part d]) - (collect-part d #f ci null 1 #hash()))) - ds - fns)) - - (define/override (collect-part-tags d ci number) - (super collect-part-tags d ci number) - (cond - [(findf external-title? (style-properties (part-style d))) - => (λ (ext-title) - (collect-content (external-title-content ext-title) ci))])) - - ; We unfortunately have to replace this method wholesale, since we need to - ; resolve the title content with `current-tag-prefixes` and - ; `current-link-render-style` properly adjusted. The implementation is - ; otherwise taken from `scribble/base-render` unmodified. - (define/override (resolve-part d ri) - (parameterize ([current-tag-prefixes - (extend-tag-prefix d (fresh-tag-resolve-context? d ri))] - [current-link-render-style (part-render-style d)]) - (when (part-title-content d) - (resolve-content (part-title-content d) d ri)) - (cond - [(findf external-title? (style-properties (part-style d))) - => (λ (ext-title) - (resolve-content (external-title-content ext-title) d ri))]) - (resolve-flow (part-blocks d) d ri) - (for ([p (part-parts d)]) - (resolve-part p ri)))) - - (define/overment (render-one part ri output-file) - (parameterize ([current-output-file output-file] - [current-top-part part]) - (inner/hijack render-one part ri output-file))) - - (define/override (render-nested-flow i part ri starting-item?) - (render-flow (nested-flow-blocks i) part ri #t)) - - (super-new))) - -;; The foundational renderer mixin used by this blog. It is an HTML renderer, -;; but it is drastically stripped down and streamlined relative to the built-in -;; renderer provided by scribble/html-render. -;; -;; This mixin does not, by itself, support all the functionality used by blog -;; posts themselves---for example, it does not support footnotes---but it is a -;; useful foundation that can be used for rendering non-post pages. -(define (blog-render-mixin %) - (class % - (define/override (current-render-mode) '(html)) - (define/override (get-suffix) #".html") - - (define external-tag-path (string->url (get-doc-search-url))) - (define/override (set-external-tag-path p) - (set! external-tag-path (string->url p))) - - ;; Returns the (absolute) path portion of the URL for the page that contains - ;; the part currently being rendered. - (define/public (get-current-site-path) - (path->string (build-path "/" (find-relative-path (get-dest-directory) - (current-output-file))))) - - ;; ------------------------------------------------------------------------- - ;; collect - - (define/public (part-whole-page? p ri) - (match (resolve-get p ri (car (part-tags p))) - ; racket doc reference; index 4 corresponds to `whole-page?` - [(? vector? dest) (vector-ref dest 4)] - ; blog references - [(? blog-page?) #t] - [(? blog-page-anchor?) #f])) - - (define/public (current-part-whole-page? d) - (eq? d (current-top-part))) - - (define/override (fresh-tag-collect-context? d ci) - (current-part-whole-page? d)) - (define/override (fresh-tag-resolve-context? d ri) - (part-whole-page? d ri)) - (define/override (fresh-tag-render-context? d ri) - (part-whole-page? d ri)) - - (define/override (collect-part-tags d ci number) - (for ([t (part-tags d)]) - (define key (generate-tag t ci)) - (define title-content - (cond - [(findf external-title? (style-properties (part-style d))) - => (λ (ext-title) - (collect-content (external-title-content ext-title) ci) - (external-title-content ext-title))] - [(part-title-content d)] - [else "???"])) - (collect-put! ci key - (if (current-part-whole-page? d) - (blog-page title-content (get-current-site-path)) - (blog-page-anchor title-content - (get-current-site-path) - (tag->anchor-name (add-current-tag-prefix key))))))) - - (define/public (collect-link-target tag ci) - (define key (generate-tag tag ci)) - (collect-put! ci key (blog-page-anchor #f - (get-current-site-path) - (tag->anchor-name (add-current-tag-prefix key))))) - - (define/private (collect-link-target-from-style style ci) - (cond - [(findf link-target? (style-properties style)) - => (λ (target) (collect-link-target (link-target-tag target) ci))])) - - (define/override (collect-nested-flow i ci) - (collect-link-target-from-style (nested-flow-style i) ci) - (super collect-nested-flow i ci)) - - (define/override (collect-paragraph i ci) - (collect-link-target-from-style (paragraph-style i) ci) - (super collect-paragraph i ci)) - - (define/override (collect-content i ci) - (when (and (element? i) (style? (element-style i))) - (collect-link-target-from-style (element-style i) ci)) - (super collect-content i ci)) - - (define/override (collect-target-element i ci) - (when (redirect-target-element? i) - (raise-arguments-error 'collect-target-element "redirect targets not supported" - "element" i)) - (collect-link-target (target-element-tag i) ci)) - - (define/override (resolve-content i d ri) - (cond - [(and external-tag-path - (link-element? i) - (style? (element-style i)) - (memq 'indirect-link (style-properties (element-style i)))) - ; Don’t resolve indirect links, or else we’ll get spurious warnings - ; about undefined tags. - (resolve-content (element-content i) d ri)] - [else - (super resolve-content i d ri)])) - - ;; ------------------------------------------------------------------------- - ;; render - - (inherit get-dest-directory - - render-block - render-flow - render-part - - number-depth) - - (define/override (render-one part ri output-file) - (define xexpr - `(html - (head (title ,(content->string (strip-aux (part-title-content part)) this part ri))) - (body ,@(render-part part ri)))) - (write-xexpr xexpr) - xexpr) - - (define/override (render-part-content part ri) - (define number (collected-info-number (part-collected-info part ri))) - (define anchors (for/list ([tag (in-list (part-tags part))]) - `(a ([name ,(tag->anchor-name - (add-current-tag-prefix - (tag-key tag ri)))])))) - (define title - (when/list (not (part-style? part 'hidden)) - `(,(header-depth->html-tag (number-depth number)) - ,@anchors - ,@(cond/list - [(part-title-content part) - => (λ (title-content) (render-content title-content part ri))])))) - `[,@title - ,@(render-flow (part-blocks part) part ri #f) - ,@(append-map (λ~> (render-part ri)) (part-parts part))]) - - (define/override (render-nested-flow i part ri starting-item?) - (define-values [tag-name attrs] (style->tag-name+attributes (nested-flow-style i) ri)) - `[(,(or tag-name 'blockquote) - ,(attributes->list attrs) - ,@(super render-nested-flow i part ri starting-item?))]) - - (define/override (render-paragraph e part ri) - (define-values [tag-name attrs] (style->tag-name+attributes (paragraph-style e) ri)) - `[(,(or tag-name 'p) - ,(attributes->list attrs) - ,@(super render-paragraph e part ri))]) - - (define/override (render-itemization e part ri) - (define style (itemization-style e)) - (define-values [tag-name attrs] (style->tag-name+attributes style ri)) - `[(,(or tag-name - (if (eq? (style-name style) 'ordered) 'ol 'ul)) - ,(attributes->list attrs) - ,@(for/list ([blocks (in-list (itemization-blockss e))]) - `(li ,@(render-flow blocks part ri #t))))]) - - (define/override (render-table e part ri starting-item?) - (define cellss (table-blockss e)) - (define num-columns - (cond - [(empty? cellss) 0] - [else - (define num-columns (length (first cellss))) - (for ([cells (in-list (rest cellss))]) - (unless (= (length cells) num-columns) - (raise-arguments-error 'render-table "table rows have inconsistent lengths" - "first row length" num-columns - "other row length" (length cells) - "first row..." (first cellss) - "other row..." cells))) - num-columns])) - - (define t-style (table-style e)) - (define col-styles (and~> (findf table-columns? (style-properties t-style)) - table-columns-styles)) - (define row-styles (and~> (findf table-rows? (style-properties t-style)) - table-rows-styles)) - (define cell-styless (and~> (findf table-cells? (style-properties t-style)) - table-cells-styless)) - - (define (check-count what in-what in-v given expected) - (unless (= given expected) - (raise-argument-error 'render-table (~a (if (< given expected) "not enough" "too many") - " " what " styles for " in-what) - (~a what " count") expected - "style count" given - (~a in-what "...") in-v))) - (when col-styles - (check-count "column" "table" e (length col-styles) num-columns)) - (when row-styles - (check-count "row" "table" e (length row-styles) (length cellss))) - (when cell-styless - (check-count "row" "table" e (length cell-styless) (length cellss)) - (for ([cells (in-list cellss)] - [cell-styles (in-list cell-styless)]) - (check-count "cell" "row" cells (length cell-styles) num-columns))) - - (define-values [tag-name attrs] (style->tag-name+attributes t-style ri)) - `[(,(or tag-name 'table) - ,(attributes->list attrs) - ,@(for/list ([cells (in-list cellss)] - [row-style (if row-styles - (in-list row-styles) - (in-cycle (in-value plain)))] - [cell-styles (if cell-styless - (in-list cell-styless) - (in-cycle (in-value #f)))]) - (define-values [tag-name attrs] (style->tag-name+attributes row-style ri)) - (list* (or tag-name 'tr) - (attributes->list attrs) - (let loop ([cells cells] - [col-styles col-styles] - [cell-styles cell-styles]) - (match cells - ['() '()] - [(list* cell (and 'cont cont) ... cells) - (define colspan (add1 (length cont))) - - (define (split-styles ss) - (if ss - (values (first ss) (drop ss colspan)) - (values plain #f))) - (define-values [col-style col-styles*] (split-styles col-styles)) - (define-values [cell-style cell-styles*] (split-styles cell-styles)) - - (define-values [col-tag-name col-attrs] (style->tag-name+attributes col-style ri)) - (define-values [cell-tag-name cell-attrs] (style->tag-name+attributes cell-style ri)) - (define attrs (~> (attributes-union col-attrs cell-attrs #:allow-override? #t) - (attributes-union (if (= colspan 1) - (attributes) - (attributes 'colspan (~a colspan))) - (table-cell-style->attributes col-style cell-style)))) - (cons (list* (or cell-tag-name col-tag-name 'td) - (attributes->list attrs) - (render-table-cell cell part ri)) - (loop cells col-styles* cell-styles*))])))))]) - - (define/private (table-cell-style->attributes col-s cell-s) - (define col-props (style-properties col-s)) - (define cell-props (style-properties cell-s)) - - (define (alignment what which) - (define (go for-what props) - (define aligns (filter (λ~> (memq which)) props)) - (match aligns - ['() #f] - [(list align) (~a "cell-align-" align)] - [_ (raise-arguments-error 'render-table (~a for-what " has multiple " what " alignments") - "alignment properties" aligns)])) - (or (go "cell" cell-props) - (go "column" col-props))) - - (define h-align (alignment "horizontal" '(left right center))) - (define v-align (alignment "vertical" '(top baseline bottom vcenter))) - (attributes 'class (string-join (filter values (list h-align v-align))))) - - (define/public (render-table-cell e part ri) - (cond - [(and (paragraph? e) - (memq 'omitable (style-properties (paragraph-style e)))) - (render-content (paragraph-content e) part ri)] - [else - (render-block e part ri #f)])) - - (define/public (render-title-link tag part ri) - (define title-content - (match (resolve-get part ri tag) - [(vector title _ ...) title] - [(blog-page title _) title] - [(blog-page-anchor title _ _) title] - [#f "???"])) - (render-content title-content part ri)) - - (define/override (render-content elem part ri) - (cond - [(string? elem) (list elem)] - [else - (define style (normalize-element-style (if (element? elem) (element-style elem) #f))) - (define-values [tag-name attrs] (style->tag-name+attributes style ri)) - - (define (wrap-for-style rendered #:attrs [attrs attrs]) - ; First, we determine what element wrappers are needed by the style. - - ; alt-tag needs a custom wrapper - (define alt-tag-wrap (if tag-name (cons tag-name (attributes)) #f)) - - ; target-url needs an 'a wrapper - (define link-wrap - (match (findf target-url? (style-properties style)) - [(target-url target) - (cons 'a (attributes 'href target))] - [#f #f])) - - ; certain symbolic styles need wrappers - (define style-name-wrap - (match (style-name style) - ['bold (cons 'strong (attributes))] - [(or 'emph 'italic) (cons 'em (attributes))] - ['tt (cons 'code (attributes))] - ['superscript (cons 'sup (attributes))] - ['subscript (cons 'sub (attributes))] - [_ #f])) - - ; Now we combine the wrappers and mix in extra attributes. - (define all-wraps - (match (filter values (list alt-tag-wrap link-wrap style-name-wrap)) - ; If there are no wrappers, but we need to add attributes, add a - ; 'span wrapper to hold them. - ['() - (if (attributes-empty? attrs) - '() - (list (cons 'span attrs)))] - ; Otherwise, add the attributes to the outermost wrapper. - [(cons (cons outer-tag outer-attrs) wraps) - (cons (cons outer-tag (attributes-union outer-attrs attrs)) wraps)])) - - ; Finally, we apply all the wrappers. - (for/fold ([rendered rendered]) - ([wrap (in-list all-wraps)]) - `[(,(car wrap) ,(attributes->list (cdr wrap)) ,@rendered)])) - - (define rendered (if (and (link-element? elem) - (null? (element-content elem))) - (render-title-link (link-element-tag elem) part ri) - (super render-content elem part ri))) - (match elem - [(target-element _ _ tag) - (tag->anchor-name (resolve-tag tag ri)) - (cons `(a ([name ,(tag->anchor-name (resolve-tag tag ri))])) - (wrap-for-style rendered))] - - [(link-element _ _ tag) - (define indirect? (memq 'indirect-link (style-properties style))) - (define dest (and (not indirect?) (resolve-get part ri tag))) - (define href - (match dest - ; racket doc reference - [(or #f (? vector?)) - (define redirect-query - (cons (cons 'tag (tag->local-redirect-query-string tag)) - (url-query external-tag-path))) - (url->string (struct-copy url external-tag-path [query redirect-query]))] - - [(blog-page _ path) - (site-path->url-string path)] - - [(blog-page-anchor _ path anchor) - (if (equal? path (get-current-site-path)) - (string-append "#" (uri-encode anchor)) - (site-path->url-string path #:fragment anchor))])) - - (define attrs* (attributes-union (attributes 'href href) attrs)) - (wrap-for-style #:attrs (attributes) `[(a ,(attributes->list attrs*) ,@rendered)])] - - [_ (wrap-for-style rendered)])])) - - (super-new))) - -;; Recognizes `paragraph`s and `element`s with a `pygments-content` style -;; property and replaces them with the result of running Pygments on the content -;; in the property. Body content and other style properties are ignored. -(define (pygments-render-mixin %) - (class/hijack % - #:hijack-methods [render-one] - - (define/overment (render-one part ri output-file) - (call-with-current-pygments-server - (λ () (inner/hijack render-one part ri output-file)))) - - (define/override (render-paragraph e part ri) - (match (findf pygments-content? (style-properties (paragraph-style e))) - [(pygments-content source language) - `[(pre ,(pygmentize source #:language language))]] - [_ (super render-paragraph e part ri)])) - - (define/override (render-content e part ri) - (match e - [(element (style _ (app (λ~>> (findf pygments-content?)) - (pygments-content source language))) - _) - (list (pygmentize source #:language language))] - [_ (super render-content e part ri)])) - - (super-new))) - -(define (blog-standalone-page-render% page-template) - (class (pygments-render-mixin (blog-render-mixin base-render%)) - (inherit render-part) - - (define/override (render-one part ri output-file) - (define xexpr - (page-template - #:title (content->string (strip-aux (part-title-content part)) this part ri) - #:body (render-part part ri))) - (write-xexpr xexpr) - xexpr) - - (super-new))) - -;; The renderer used for actual blog posts, with all the bells and whistles. -(define blog-post-render% - (class (pygments-render-mixin (blog-render-mixin base-render%)) - (inherit render-content - render-flow - render-part) - - ; We don’t render blog posts directly to HTML because we need to be able to - ; render them in different ways on different pages, so we generate `.info` - ; files containing serialized `rendered-post` structures, instead. - (define/override (get-suffix) #".info") - - (define/override (get-current-site-path) - (define part (current-top-part)) - (post-path (get-required-style-prop post-date? (style-properties (part-style part))) - (content->string (strip-aux (part-title-content part))))) - - (define/override (render-one part ri output-file) - (define props (style-properties (part-style part))) - (define title-str (content->string (strip-aux (part-title-content part)) this part ri)) - (define title-content (render-content (part-title-content part) part ri)) - (define body-content (append (render-flow (part-blocks part) part ri #t) - (append-map (λ~> (render-part ri)) (part-parts part)))) - (define post - (rendered-post title-str - title-content - (get-required-style-prop post-date? props) - (post-tags-tags (get-required-style-prop post-tags? props)) - body-content)) - (write (serialize post)) - post) - - (define/private (get-required-style-prop pred? props) - (or (findf pred? props) - (raise-arguments-error 'render "missing required style property on main part" - "output file" (current-output-file) - "expected" (unquoted-printing-string (~a (contract-name pred?))) - "properties" props))) - - (super-new))) diff --git a/blog/build/render/util.rkt b/blog/build/render/util.rkt deleted file mode 100644 index 6776780..0000000 --- a/blog/build/render/util.rkt +++ /dev/null @@ -1,60 +0,0 @@ -#lang racket/base - -(require racket/contract - racket/format - racket/match - racket/string - scribble/core - scribble/private/literal-anchor - syntax/parse/define) - -(provide when/list - when/list* - unless/list - unless/list* - cond/list - (contract-out - [to-slug (-> string? string?)] - - [normalize-element-style (-> element-style? style?)] - [tag->anchor-name (-> tag? string?)])) - -;; ----------------------------------------------------------------------------- - -(define (to-slug s) - (define rx #px"[^a-z0-9]+") - (regexp-replace* rx (string-trim (string-downcase s) rx) "-")) - -;; ----------------------------------------------------------------------------- -;; xexprs - -(define-simple-macro (when/list condition:expr body ...+) - (if condition (list (let () body ...)) '())) -(define-simple-macro (when/list* condition:expr body ...+) - (if condition (let () body ...) '())) -(define-simple-macro (unless/list condition:expr body ...+) - (if condition '() (list (let () body ...)))) -(define-simple-macro (unless/list* condition:expr body ...+) - (if condition '() (let () body ...))) -(define-simple-macro (cond/list clause ...) - (cond clause ... [else '()])) - -;; ----------------------------------------------------------------------------- -;; scribble - -(define (normalize-element-style v) - (cond - [(not v) plain] - [(style? v) v] - [else (style v '())])) - -(define tag->anchor-name - (match-lambda - [(literal-anchor anchor-name) anchor-name] - [(list 'part tag) (to-slug (~a tag))] - [(list sym (cons 'prefixable tag)) (tag->anchor-name (list sym tag))] - [(list sym tag) - ; This anchor naming scheme does not in any way create unique anchors, but - ; that should be okay for internal references in this use case, and having - ; pretty URLs is a nice feature. - (to-slug (~a sym " " tag))])) diff --git a/blog/build/serve.rkt b/blog/build/serve.rkt deleted file mode 100644 index 9f0c70c..0000000 --- a/blog/build/serve.rkt +++ /dev/null @@ -1,17 +0,0 @@ -#lang racket/base - -(require web-server/http/empty - web-server/servlet-env - "../paths.rkt") - -(provide start-server) - -(define (start-server) - (serve/servlet (λ (req) (response/empty #:code 404)) - #:extra-files-paths (list output-dir) - #:port 3000 - #:command-line? #t - #:banner? #t)) - -(module+ main - (start-server)) diff --git a/blog/info.rkt b/blog/info.rkt deleted file mode 100644 index 5c3c2e6..0000000 --- a/blog/info.rkt +++ /dev/null @@ -1,13 +0,0 @@ -#lang info - -(define collection "blog") - -(define deps - '("base" - ["commonmark-lib" #:version "1.1"] - "racket-index" - "scribble-lib" - "threading-lib" - "web-server-lib")) -(define build-deps - '("at-exp-lib")) diff --git a/blog/lang/base.rkt b/blog/lang/base.rkt deleted file mode 100644 index f32ee00..0000000 --- a/blog/lang/base.rkt +++ /dev/null @@ -1,256 +0,0 @@ -#lang racket/base - -(require (for-syntax racket/base - racket/match - syntax/location) - racket/contract - racket/match - racket/string - (prefix-in scribble: scribble/base) - scribble/base - scribble/core - scribble/decode - scribble/html-properties - (only-in scribble/manual deftech tech) - syntax/parse/define - threading - - "../paths.rkt" - "footnote.rkt" - "metadata.rkt") - -(provide (all-from-out "footnote.rkt") - (struct-out post-date) - (struct-out post-tags) - (struct-out table-rows) - (struct-out external-title) - blog-post - deftech - tech - (contract-out [section (->* [] [#:tag (or/c string? (listof string?) #f) - #:tag-prefix (or/c string? module-path? #f) - #:style (or/c style? symbol? (listof symbol?) #f) - #:depth exact-nonnegative-integer?] - #:rest (listof pre-content?) - part-start?)] - [subsubsubsection (->* [] [#:tag (or/c string? (listof string?) #f) - #:tag-prefix (or/c string? module-path? #f) - #:style (or/c style? symbol? (listof symbol?) #f)] - #:rest (listof pre-content?) - part-start?)] - [secref (->* [string?] - [#:doc (or/c module-path? #f) - #:post (or/c string? #f) - #:tag-prefixes (or/c (listof string?) #f)] - element?)] - [seclink (->* [string?] - [#:doc (or/c module-path? #f) - #:post (or/c string? #f) - #:tag-prefixes (or/c (listof string?) #f) - #:indirect? any/c] - #:rest (listof pre-content?) - element?)] - [other-post (-> string? element?)] - [other-post* (-> string? pre-content? ... element?)] - - [code (-> content? ... element?)] - [code-block (-> content? ... block?)] - (struct pygments-content ([source string?] [language string?])) - [pygments (-> #:language string? string? ... element?)] - [pygments-block (-> #:language string? string? ... block?)] - [haskell (-> string? ... element?)] - [haskell-block (-> string? ... block?)] - - [blog-tag (-> string? element?)] - - [wikipedia (-> pre-content? ... element?)] - [github-repo (-> string? element?)] - [github-repo* (-> string? pre-content? ... element?)] - [hackage-package (-> string? element?)] - [hackage-package* (-> string? pre-content? ... element?)] - [hackage-module (-> string? string? element?)] - [hackage-module* (-> string? string? pre-content? ... element?)])) - -;; ----------------------------------------------------------------------------- - -(module* lang racket/base - (require scribble/doclang2 - (except-in scribble/base seclink secref section) - (submod "..")) - (provide (all-from-out scribble/doclang2 - scribble/base - (submod "..")))) - -(module* reader syntax/module-reader - #:language get-lang-mod-path - #:read scribble:read-inside - #:read-syntax scribble:read-syntax-inside - #:whole-body-readers? #t - #:info (scribble-base-reader-info) - #:language-info (scribble-base-language-info) - - (require (for-syntax racket/base) - racket/runtime-path - (prefix-in scribble: scribble/reader) - (only-in scribble/base/reader - scribble-base-reader-info - scribble-base-language-info)) - - (define-runtime-module-path-index lang-mpi '(submod ".." lang)) - (define (get-lang-mod-path) - #;(cons 'submod (resolved-module-path-name - (module-path-index-resolve - lang-mpi - #f - #'(submod ".." lang)))) - ;; see - '(submod blog/lang/base lang))) - -;; ----------------------------------------------------------------------------- - -(define (section #:tag [tag #f] - #:tag-prefix [prefix #f] - #:style [style #f] - #:depth [depth 0] - . pre-content) - (define content (decode-content pre-content)) - (~> (apply scribble:section content - ; `scribble:section` already handles generating a tag from the - ; decoded content if necessary, but it preprocesses it by replacing - ; most non-ASCII characters with underscores. This is to avoid - ; causing trouble in generated URLs, but it’s arguably the wrong - ; layer at which to do this escaping, and indeed, we have our own - ; scheme for generating anchor names from tags in the renderer. - ; So if no tag is provided, we handle generating one ourselves - ; here, without any escaping. - #:tag (or tag (content->string content)) - #:tag-prefix prefix - #:style style) - (struct-copy part-start _ [depth depth]))) - -(define (subsubsubsection #:tag [tag #f] - #:tag-prefix [prefix #f] - #:style [style #f] - . pre-content) - (apply section pre-content #:depth 3 - #:tag tag #:tag-prefix prefix #:style style)) - -(define (secref tag - #:doc [module-path #f] - #:post [post-path #f] - #:tag-prefixes [prefixes #f]) - (scribble:secref - tag - #:doc module-path - #:tag-prefixes (if post-path - (cons (blog-post-path->tag-prefix post-path) (or prefixes '())) - prefixes))) - -(define (seclink tag - #:doc [module-path #f] - #:post [post-path #f] - #:tag-prefixes [prefixes #f] - #:indirect? [indirect? #f] - . pre-content) - (apply scribble:seclink tag pre-content - #:doc module-path - #:tag-prefixes (if post-path - (cons (blog-post-path->tag-prefix post-path) (or prefixes '())) - prefixes) - #:indirect? indirect?)) - -(define (other-post post-path) - (secref "top" #:post post-path)) -(define (other-post* post-path . pre-content) - (apply seclink "top" #:post post-path pre-content)) - -;; ----------------------------------------------------------------------------- - -(define (code . content) - (element (style #f (list (alt-tag "code"))) content)) - -(define (code-block . content) - (paragraph (style #f (list (alt-tag "pre"))) (apply code content))) - -(struct pygments-content (source language) #:transparent) - -(define (pygments #:language language . strs) - (element (style #f (list (pygments-content (string-append* strs) language))) '())) -(define (pygments-block #:language language . strs) - (paragraph (style #f (list (pygments-content (string-append* strs) language))) '())) - -(define (haskell . strs) - (apply pygments #:language "haskell" strs)) -(define (haskell-block . strs) - (apply pygments-block #:language "haskell" strs)) - -;; ----------------------------------------------------------------------------- - -(define (blog-tag tag-str) - (hyperlink (index-path #:tag tag-str) tag-str)) - -(define (add-blog-post-properties title date tags) - (define title-style (title-decl-style title)) - (struct-copy title-decl title - [style (style (style-name title-style) - (list* date - (post-tags tags) - (style-properties title-style)))])) - -(define-for-syntax (infer-date stx) - (match (path->string (syntax-source-file-name stx)) - [(regexp #px"^(\\d{4})-(\\d{2})-(\\d{2})-" - (list _ - (app string->number year) - (app string->number month) - (app string->number day))) - #`(post-date '#,year '#,month '#,day)] - [_ (raise-syntax-error #f "file name does not start with date" stx)])) - -(define-syntax-parser blog-post - [(_ {~alt {~once {~var title-expr (expr/c #'title-decl? #:name "title expression")}} - {~once {~seq #:tags {~var post-tags (expr/c #'(listof string?) #:name "tags expression")}}}} - ...) - #`(add-blog-post-properties title-expr.c - #,(infer-date this-syntax) - post-tags.c)]) - -;; ----------------------------------------------------------------------------- - -(define (wikipedia . pre-content) - (define content (decode-content pre-content)) - (define words - (match (string-split (content->string content)) - ; capitalize the first word to match wikipedia naming conventions - [(cons word words) - (cons (string-append (string (char-upcase (string-ref word 0))) - (substring word 1)) - words)] - ['() '()])) - (hyperlink (string-append "https://en.wikipedia.org/wiki/" (string-join words "_")) content)) - -(define (github-repo user+repo) - (match user+repo - [(regexp #px"^[^/]+/(.+)$" (list _ repo)) - (github-repo* user+repo repo)] - [repo - (github-repo* repo repo)])) -(define (github-repo* repo . pre-content) - (define user+repo (if (string-contains? repo "/") repo (string-append "lexi-lambda/" repo))) - (apply hyperlink (string-append "https://github.com/" user+repo) pre-content)) - -;; ----------------------------------------------------------------------------- -;; hackage - -(define (hackage-package package-name) - (hackage-package* package-name package-name)) -(define (hackage-package* package-name . pre-content) - (apply hyperlink (string-append "https://hackage.haskell.org/package/" package-name) pre-content)) - -(define (hackage-module package+version module-name) - (hackage-module* package+version module-name (tt module-name))) -(define (hackage-module* package+version module-name . pre-content) - (apply hyperlink - (string-append "https://hackage.haskell.org/package/" package+version - "/docs/" (string-replace module-name "." "-") ".html") - pre-content)) diff --git a/blog/lang/footnote.rkt b/blog/lang/footnote.rkt deleted file mode 100644 index f3ddc28..0000000 --- a/blog/lang/footnote.rkt +++ /dev/null @@ -1,160 +0,0 @@ -#lang racket/base - -(require (for-syntax racket/base - racket/syntax) - racket/contract - racket/format - racket/list - racket/match - scribble/core - scribble/decode - scribble/html-properties - syntax/parse/define - threading - - "metadata.rkt") - -(provide define-footnote - (contract-out - [make-footnote-tag (->* [string?] - [#:post (or/c string? #f) - #:tag-prefixes (listof string?)] - tag?)] - [footnote-ref (-> string? content?)] - [footnote-link (-> string? pre-content? ... link-element?)] - [footnote-decl (-> string? pre-flow? ... part-collect-decl?)] - [footnote-collect-element (-> string? (listof block?) content?)] - [footnotes-section (-> part?)])) - -(define (make-footnote-tag tag-str - #:post [post-path #f] - #:tag-prefixes [tag-prefixes '()]) - `(footnote ,(taglet-add-prefix - (cons 'prefixable - (if post-path - (cons (blog-post-path->tag-prefix post-path) tag-prefixes) - tag-prefixes)) - tag-str))) - -(define footnote-refs-key (gensym 'footnote-refs)) -(define footnote-ref-targets-key (gensym 'footnote-ref-targets)) -(define footnote-flows-key (gensym 'footnote-flows)) - -(define (footnote-ref tag) - (traverse-element - (λ (get set) - (define refs (get footnote-refs-key '())) - (define number (cond - [(index-of refs tag) - => (λ (idx) (- (length refs) idx))] - [else - (set footnote-refs-key (cons tag refs)) - (add1 (length refs))])) - - (define all-ref-targets (get footnote-ref-targets-key (hash))) - (define ref-targets (hash-ref all-ref-targets tag '())) - (define ref-target `(footnote-ref (prefixable ,tag ,(~a (add1 (length ref-targets)))))) - (set footnote-ref-targets-key - (hash-set all-ref-targets - tag - (cons ref-target ref-targets))) - - (link-element (style 'superscript (list (link-target ref-target))) - (~a number) - (make-footnote-tag tag))))) - -(define (footnote-link tag . pre-content) - (link-element #f (decode-content pre-content) (make-footnote-tag tag))) - -(define (footnote-decl tag . pre-flows) - (part-collect-decl - (element #f (footnote-collect-element tag (decode-flow pre-flows))))) - -(define (footnote-collect-element tag blocks) - (traverse-element - (λ (get set) - (set footnote-flows-key - (cons (cons tag blocks) - (get footnote-flows-key '()))) - '()))) - -(define (footnotes-section) - (define (generate get set) - (define refs (get footnote-refs-key '())) - (define ref-targets (get footnote-ref-targets-key (hash))) - (define-values [referenced-flows other-flows] - (partition (λ~> car (member refs)) (get footnote-flows-key '()))) - - ; Tacks the ↩ “return to reference” links onto the end of the footnote’s - ; flow, folding them into a trailing paragraph if at all possible. - (define (add-ref-links blocks ref-tags) - (cond - [(empty? ref-tags) blocks] - [else - (define ref-links (~> (for/list ([ref-tag (in-list ref-tags)]) - (link-element #f "↩" ref-tag)) - (add-between " "))) - (reverse - (match (reverse blocks) - [(cons (paragraph p-style p-content) blocks) - (cons (paragraph p-style (list* p-content " " ref-links)) blocks)] - [blocks - (cons (paragraph plain ref-links) blocks)]))])) - - (define (build-item number tag+blocks) - (match-define (cons tag blocks) tag+blocks) - (nested-flow - (style #f (list* (alt-tag "li") - (link-target (make-footnote-tag tag)) - (if number - (list (attributes `([value . ,(~a number)]))) - '()))) - (add-ref-links blocks (reverse (hash-ref ref-targets tag '()))))) - - ; Build the footnote items, starting with the ones that were actually - ; referenced. If a footnote was referenced that doesn’t actually exist, we - ; want to skip its number in the output list, which requires we set the - ; `value` attribute on the generated `li`. The `skipped?` argument tracks - ; whether or not this is necessary for the next `li`. - (define items - (let loop ([refs (reverse refs)] - [number 1] - [skipped? #f]) - (match refs - [(cons ref refs) - (match (assoc ref referenced-flows) - [(? pair? flow) - (cons (build-item (and skipped? number) flow) - (loop refs (add1 number) #f))] - [#f (loop refs (add1 number) #t)])] - - ; We’re done outputting the referenced footnotes. If there were any - ; unreferenced ones, just tack them onto the end. - ['() (match other-flows - ['() '()] - [(cons flow flows) - (cons (build-item (and skipped? number) flow) - (map (λ~>> (build-item #f)) flows))])]))) - - (nested-flow (style #f (list (alt-tag "ol") - (attributes '([class . "footnotes"])))) - items)) - (part #f - (list `(part ,(generated-tag))) - #f - (style #f '(unnumbered hidden toc-hidden)) - '() - (list (traverse-block generate)) - '())) - -(define-syntax-parser define-footnote - [(_ ref-id:id {~optional {~seq #:tag tag-e}} pre-flow ...) - #:declare tag-e (expr/c #'string? #:name "tag") - #:with note-ref-id (format-id #'ref-id "note:~a" #'ref-id #:subs? #t) - #:with note-link-id (format-id #'ref-id "note:~a*" #'ref-id #:subs? #t) - #`(begin - (define note-tag {~? tag-e.c '#,(symbol->string (syntax-e #'ref-id))}) - (define (note-ref-id) (footnote-ref note-tag)) - (define (note-link-id . pre-content) - (apply footnote-link note-tag pre-content)) - (footnote-decl note-tag pre-flow ...))]) diff --git a/blog/lang/metadata.rkt b/blog/lang/metadata.rkt deleted file mode 100644 index 653e77e..0000000 --- a/blog/lang/metadata.rkt +++ /dev/null @@ -1,66 +0,0 @@ -#lang racket/base - -(require racket/contract - racket/format - racket/serialize - racket/string - scribble/core - scribble/tag) - -(provide (struct-out post-date) - - (contract-out - (struct post-tags ([tags (listof string?)])) - [post-date->string (-> post-date? string?)] - [post-date->strings (-> post-date? (list/c string? string? string?))] - - (struct link-target ([tag tag?])) - (struct css-styles ([assoc (listof (cons/c symbol? string?))])) - (struct table-rows ([styles (listof style?)])) - (struct external-title ([content content?])) - - [taglet-add-prefix (-> (or/c (or/c string? symbol?) - (listof (or/c string? symbol?))) - taglet? - taglet?)] - [blog-post-path->tag-prefix (-> (and/c string? relative-path?) string?)])) - -(serializable-struct post-date (year month day) #:transparent - #:guard (struct-guard/c exact-integer? (integer-in 1 12) (integer-in 1 31))) -(struct post-tags (tags) #:transparent) - -(define (post-date->string date) - (string-join (post-date->strings date) "-")) - -(define (post-date->strings date) - (list (~a (post-date-year date)) - (~r #:min-width 2 #:pad-string "0" (post-date-month date)) - (~r #:min-width 2 #:pad-string "0" (post-date-day date)))) - -;; A style property used to mark an arbitrary block or element as a link -;; destination, as if it were a `target-element` with the given tag. -(struct link-target (tag) #:transparent) - -;; A style property like `attributes`, but for inline CSS styles. Unlike using -;; an `attributes` property with a `style` entry, multiple `css-styles` -;; properties are properly merged. -(struct css-styles (assoc) #:transparent) - -;; A style property like `table-cells` and `table-columns`, but its styles -;; apply to the table’s elements. -(struct table-rows (styles) #:transparent) - -;; A style property that can be attached to a `part` to supply alternate -;; `content` to use when the part is linked to instead of the part’s usual -;; title content. -(struct external-title (content) #:transparent) - -(define (taglet-add-prefix prefix taglet) - (if (list? prefix) - (foldr taglet-add-prefix taglet prefix) - (if (list? taglet) - (cons prefix taglet) - (list prefix taglet)))) - -(define (blog-post-path->tag-prefix path) - (~a `(blog ,path))) diff --git a/blog/markdown.rkt b/blog/markdown.rkt deleted file mode 100644 index a84fac5..0000000 --- a/blog/markdown.rkt +++ /dev/null @@ -1,208 +0,0 @@ -#lang racket/base - -(require commonmark/parse - (prefix-in md: commonmark/struct) - racket/contract - racket/format - racket/list - racket/match - racket/string - (except-in scribble/base section secref seclink) - scribble/core - scribble/decode - scribble/decode-struct - scribble/html-properties - threading - (only-in xml - cdata - string->xexpr) - - "lang/base.rkt") - -(provide (contract-out - [parse-markdown-post (-> input-port? part?)])) - -;; ----------------------------------------------------------------------------- - -(define (parse-markdown-post in) - (parameterize ([current-parse-footnotes? #t]) - (define title-info (parse-title-info in)) - (define doc (read-document in)) - (document->part doc title-info))) - -(define (parse-title-info in) - (match-define (list _ title-bytes) (regexp-match #px"^ Title: ([^\n]+)\n" in)) - (define external-title-str - (match (regexp-try-match #px"^ External title: ([^\n]+)\n" in) - [(list _ external-title-bytes) (bytes->string/utf-8 external-title-bytes)] - [_ #f])) - (match-define (list _ year-bytes month-bytes day-bytes) - (regexp-match #px"^ Date: ([0-9]{4})-([0-9]{2})-([0-9]{2})[^\n]*\n" in)) - (match-define (list _ tags-bytes) - (regexp-match #px"^ Tags: ([^,\n]+(?:, [^,\n]+)*)\n\n" in)) - - (match-define (md:document (list (md:paragraph title-content)) '()) - (string->document (bytes->string/utf-8 title-bytes))) - (define tags (string-split (bytes->string/utf-8 tags-bytes) ", " #:trim? #f)) - - (define base-props (list (post-date (bytes->number year-bytes) - (bytes->number month-bytes) - (bytes->number day-bytes)) - (post-tags tags))) - (title #:style (style #f (if external-title-str - (cons (external-title external-title-str) base-props) - base-props)) - (render-inline title-content))) - -(define (bytes->number bs) - (string->number (bytes->string/utf-8 bs))) - -;; ----------------------------------------------------------------------------- - -(define (document->part doc title-decl) - (define part-info (part-start 0 - (title-decl-tag-prefix title-decl) - (title-decl-tags title-decl) - (title-decl-style title-decl) - (title-decl-content title-decl))) - (match-define-values [main-part '()] (render-part (md:document-blocks doc) part-info)) - (struct-copy part main-part - [to-collect (append (render-footnote-definitions (md:document-footnotes doc)) - (part-to-collect main-part))] - [parts (append (part-parts main-part) (list (footnotes-section)))])) - -(define (render-part blocks part-info) - (define (collect-initial-blocks initial-blocks blocks) - (match blocks - ['() (finish-part (reverse initial-blocks) '() '())] - [(cons (md:heading _ depth) _) - (if (> depth (part-start-depth part-info)) - (collect-sub-parts (reverse initial-blocks) '() blocks) - (finish-part (reverse initial-blocks) '() blocks))] - [(cons block blocks) - (collect-initial-blocks (cons (render-block block) initial-blocks) blocks)])) - - (define (collect-sub-parts initial-blocks parts blocks) - (match blocks - [(cons (md:heading content depth) blocks) - #:when (> depth (part-start-depth part-info)) - (define-values [part blocks*] - (render-part blocks - (make-part-info #:title (render-inline content) - #:depth (add1 (part-start-depth part-info))))) - (collect-sub-parts initial-blocks (cons part parts) blocks*)] - [_ - (finish-part initial-blocks (reverse parts) blocks)])) - - (define (finish-part initial-blocks parts blocks) - (define part-tags (if (empty? (part-start-tags part-info)) - (list `(part ,(make-generated-tag))) - (part-start-tags part-info))) - (define title-content (part-start-title part-info)) - (values (part (part-start-tag-prefix part-info) - part-tags - title-content - (part-start-style part-info) - (make-part-to-collect (first part-tags) title-content) - initial-blocks - parts) - blocks)) - - (collect-initial-blocks '() blocks)) - -(define (render-block block) - (match block - [(? md:thematic-break?) - (paragraph (style #f (list (alt-tag "hr"))) '())] - [(md:code-block content info-string) - (define language (and~>> info-string (regexp-match #px"^[^ \t\r\n]+") first)) - (if language - (pygments-block content #:language language) - (code-block content))] - [(md:html-block content) - (xexpr->block (string->xexpr content))] - [(md:paragraph content) - (paragraph plain (render-inline content))] - [(md:blockquote blocks) - (nested-flow (style 'nested '()) (render-flow blocks))] - [(md:itemization blockss _ start-num) - (itemization (match start-num - [#f plain] - [1 (style 'ordered '())] - [_ (style 'ordered (list (attributes (list (cons 'start (~a start-num))))))]) - (map render-flow blockss))])) - -(define (render-flow blocks) - (map render-block blocks)) - -(define (render-inline content) - (match content - [(? string?) content] - [(? list?) (render-inlines content)] - [(? md:line-break?) (linebreak)] - [(md:bold content) (element 'bold (render-inline content))] - [(md:italic content) (element 'italic (render-inline content))] - [(md:code content) (code content)] - [(md:link content dest _) - (element (style #f (list (make-target-url dest))) (render-inline content))] - [(md:image description source title) - (image-element #f (render-inline description) source '() 1)] - [(md:html content) - (raise-arguments-error 'render-inline "unhandled HTML span" "content" content)] - [(md:footnote-reference label) - (footnote-ref label)])) - -; Hacky special case for some inline HTML that shows up in a couple posts. -(define (render-inlines contents) - (match contents - ['() '()] - [(cons (md:html "") contents) - (define-values [inner-contents contents*] (scan-inlines/html-close contents "")) - (cons (superscript (render-inlines inner-contents)) (render-inlines contents*))] - [(cons (md:html "") contents) - (define-values [inner-contents contents*] (scan-inlines/html-close contents "")) - (cons (code (render-inlines inner-contents)) (render-inlines contents*))] - [(cons content contents) - (cons (render-inline content) (render-inlines contents))])) -(define (scan-inlines/html-close contents close-tag) - (let loop ([inner-contents '()] - [contents contents]) - (match contents - ['() (values (reverse inner-contents) '())] - [(cons (md:html str) contents) - #:when (string=? str close-tag) - (values (reverse inner-contents) contents)] - [(cons inner-content contents) - (loop (cons inner-content inner-contents) contents)]))) - -(define (xexpr->block xexpr) - (match xexpr - [(list (? symbol? tag) (list (list (? symbol? attr-name) (? string? attr-val)) ...) xexprs ...) - (paragraph (style #f (list (alt-tag (symbol->string tag)) - (attributes (map cons attr-name attr-val)))) - (map xexpr->content xexprs))])) -(define (xexpr->content xexpr) - (match xexpr - [(? string?) xexpr] - [(list (? symbol? tag) (list (list (? symbol? attr-name) (? string? attr-val)) ...) xexpr ...) - (element (style #f (list (alt-tag (symbol->string tag)) - (attributes (map cons attr-name attr-val)))) - (map xexpr->content xexpr))])) - -(define (render-footnote-definitions defns) - (for/list ([defn (in-list defns)]) - (match-define (md:footnote-definition blocks label) defn) - (footnote-collect-element label (render-flow blocks)))) - -(define (make-part-info #:title title-content #:depth depth) - (struct-copy part-start (section title-content) [depth depth])) - -; Taken from scribble/decode. -(define (make-part-to-collect tag title-content) - (list (index-element - #f '() tag - (list (clean-up-index-string - (regexp-replace #px"^\\s+(?:(?:A|An|The)\\s)?" - (content->string title-content) ""))) - (list (element #f title-content)) - (make-part-index-desc)))) diff --git a/blog/paths.rkt b/blog/paths.rkt deleted file mode 100644 index 6d26ced..0000000 --- a/blog/paths.rkt +++ /dev/null @@ -1,68 +0,0 @@ -#lang racket/base - -(require net/url - racket/contract - racket/format - racket/runtime-path - racket/string - - "build/render/util.rkt" - "lang/metadata.rkt") - -(provide (contract-out - [build-dir path?] - [output-dir path?] - [posts-dir path?] - - [site-path? predicate/c] - [site-path->url-string (->* [site-path?] [#:fragment (or/c string? #f)] string?)] - [full-url (->* [site-path?] [#:fragment (or/c string? #f)] string?)] - - [index-path (->* [] [exact-positive-integer? - #:tag (or/c string? #f) - #:file? any/c] - site-path?)] - [post-path (->* [post-date? string?] [#:file? any/c] site-path?)] - [feed-path (->* [(or/c 'atom 'rss)] [#:tag (or/c string? #f)] site-path?)])) - -(define-runtime-path build-dir-base "../build") -(define-runtime-path output-dir-base "../output") -(define-runtime-path posts-dir "posts") - -(define build-dir (simplify-path build-dir-base #f)) -(define output-dir (simplify-path output-dir-base #f)) - -(define (site-path? v) - (and (string? v) - (absolute-path? v))) - -(define (site-path->url-string path #:fragment [fragment #f]) - (url->string (struct-copy url (path->url path) - [scheme #f] - [host #f] - [fragment fragment]))) - -(define (full-url path #:fragment [fragment #f]) - (url->string (struct-copy url (path->url path) - [scheme "https"] - [host "lexi-lambda.github.io"] - [fragment #f]))) - -(define (index-path [page-number 1] - #:tag [tag #f] - #:file? [file? #f]) - (if tag - (~a "/tags/" - (to-slug tag) - (if (= page-number 1) "" (~a "-" page-number)) - ".html") - (if (= page-number 1) - (if file? "/index.html" "/") - (~a "/index-" page-number ".html")))) - -(define (post-path date title #:file? [file? #f]) - (~a "/blog/" (string-join (post-date->strings date) "/") - "/" (to-slug title) "/" (if file? "index.html" ""))) - -(define (feed-path format #:tag [tag #f]) - (~a "/feeds/" (if tag (to-slug tag) "all") "." format ".xml")) diff --git a/blog/posts/2015-07-18-automatically-deploying-a-frog-powered-blog-to-github-pages.md b/blog/posts/2015-07-18-automatically-deploying-a-frog-powered-blog-to-github-pages.md deleted file mode 100644 index 6cb0bc4..0000000 --- a/blog/posts/2015-07-18-automatically-deploying-a-frog-powered-blog-to-github-pages.md +++ /dev/null @@ -1,154 +0,0 @@ - Title: Automatically deploying a Frog-powered blog to GitHub pages - Date: 2015-07-18T19:09:01 - Tags: racket, frog, meta - -So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses [Greg Hendershott][greghendershott]'s fantastic [Frog][frog] tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via [Travis CI][travis], so my blog is always up-to-date. - -# Setting up Frog - -I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple `raco pkg install frog` followed by `raco frog --init` and `raco frog -bp` created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is `raco frog -n "Post Title"`, and you're good to go. - -By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use [Sass][sass] for my stylesheets, potentially with support for [CoffeeScript][coffeescript] later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used [Gulp][gulp] in conjunction with [NPM][npm] for build and dependency management. - -Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control. - -# Configuring automatic deployment with Travis - -Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being [this Gist](https://gist.github.com/domenic/ec8b0fc8ab45f39403dd), which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special `gh-pages` branch. - -To make this easy, Frog can be configured to output to a separate directory via the `.frogrc` configuration file. I chose to output to the `out` directory: - -``` -output-dir = out -``` - -I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch. - -``` -$ cd out -$ git init -$ git add . -$ git commit -m "Deploy to GitHub Pages" -$ git push --force "$REMOTE_URL" master:gh-pages -``` - -The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's [encryption keys][travis-encryption] along with a GitHub [personal access token][github-access-token]. Just install the Travis CLI client, copy the access token, and run a command: - -``` -$ gem install travis -$ travis encrypt GH_TOKEN= -``` - -The output of that command is an encrypted value to be placed in an environment variable in the project's `.travis.yml` configuration file. The URL for the repository on GitHub will also need to be specified as well: - -```yaml -env: - global: - - GH_REF: 'github.com//.git' - - secure: -``` - -Now all that's left is configuring the `.travis.yml` to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to `python`, then installed Racket and Frog as pre-installation steps. - -```yaml -env: - global: - - GH_REF: 'github.com//.git' - - secure: - - RACKET_DIR: '~/racket' - - RACKET_VERSION: '6.2' - -before_install: -- git clone https://github.com/greghendershott/travis-racket.git -- cat travis-racket/install-racket.sh | bash -- export PATH="${RACKET_DIR}/bin:${PATH}" - -install: -- raco pkg install --deps search-auto frog -``` - -(It might be worth noting that Greg Hendershott *also* maintains the repository that contains the above Travis build script!) - -Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses `master`, not `gh-pages`. Obviously, I didn't want Travis running on my `master` branch, since it would be deploying to that, so I added a branch whitelist: - -```yaml -branches: - only: - - source -``` - -All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this: - -```bash -#!/bin/bash -set -ev # exit with nonzero exit code if anything fails - -# clear the output directory -rm -rf out || exit 0; - -# build the blog files + install pygments for highlighting support -npm install -npm run build -pip install pygments -raco frog --build - -# go to the out directory and create a *new* Git repo -cd out -git init - -# inside this git repo we'll pretend to be a new user -git config user.name "Travis CI" -git config user.email "" - -# The first and only commit to this new Git repo contains all the -# files present with the commit message "Deploy to GitHub Pages". -git add . -git commit -m "Deploy to GitHub Pages" - -# Force push from the current repo's master branch to the remote -# repo. (All previous history on the branch will be lost, since we are -# overwriting it.) We redirect any output to /dev/null to hide any sensitive -# credential data that might otherwise be exposed. -git push --force --quiet "https://${GH_TOKEN}@${GH_REF}" master > /dev/null 2>&1 -``` - -For reference, my final `.travis.yml` looked like this: - -```yaml -language: python -python: -- '3.4' - -branches: - only: - - source - -env: - global: - - GH_REF: 'github.com/lexi-lambda/lexi-lambda.github.io.git' - - secure: - - RACKET_DIR: '~/racket' - - RACKET_VERSION: '6.2' - -before_install: -- git clone https://github.com/greghendershott/travis-racket.git -- cat travis-racket/install-racket.sh | bash -- export PATH="${RACKET_DIR}/bin:${PATH}" - -install: -- raco pkg install --deps search-auto frog - -script: bash ./deploy.sh -``` - -That's it! Now I have a working blog that I can publish just by pushing to the `source` branch on GitHub. - -[coffeescript]: http://coffeescript.org -[frog]: https://github.com/greghendershott/frog -[github-access-token]: https://github.com/settings/tokens -[greghendershott]: http://www.greghendershott.com -[gulp]: http://gulpjs.com -[npm]: https://www.npmjs.com -[sass]: http://sass-lang.com -[travis]: https://travis-ci.org -[travis-encryption]: http://docs.travis-ci.com/user/encryption-keys/ diff --git a/blog/posts/2015-08-22-deploying-racket-applications-on-heroku.md b/blog/posts/2015-08-22-deploying-racket-applications-on-heroku.md deleted file mode 100644 index e541dcd..0000000 --- a/blog/posts/2015-08-22-deploying-racket-applications-on-heroku.md +++ /dev/null @@ -1,88 +0,0 @@ - Title: Deploying Racket applications on Heroku - Date: 2015-08-22T14:47:49 - Tags: racket, heroku, 12factor - -[Heroku][heroku] is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku. - -# Building the server - -Racket's [web-server][racket-web-server] package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going: - -```racket -#lang racket - -(require web-server/servlet - web-server/servlet-env) - -(define (start req) - (response/xexpr - '(html (head (title "Racket Heroku App")) - (body (h1 "It works!"))))) - -(serve/servlet start #:servlet-path "/") -``` - -Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the `PORT` environment variable. We can access this using the Racket `getenv`[racket] function. - -Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass `#f` for the `#:listen-ip` argument. - -```racket -(define port (if (getenv "PORT") - (string->number (getenv "PORT")) - 8080)) -(serve/servlet start - #:servlet-path "/" - #:listen-ip #f - #:port port) -``` - -Also, by default, `serve/servlet`[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off. - -```racket -(serve/servlet start - #:servlet-path "/" - #:listen-ip #f - #:port port - #:command-line? #t) -``` - -That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes. - -# Setting up our app for Heroku - -The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack: - -```sh -$ git init -$ heroku git:remote -a racket-heroku-sample -$ heroku buildpacks:set https://github.com/lexi-lambda/heroku-buildpack-racket -``` - -We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the `RACKET_VERSION` environment variable as follows: - -```sh -$ heroku config:set RACKET_VERSION=6.2.1 -``` - -Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process. - -Specifically, we just want to run our `serve.rkt` module. The Racket buildpack installs the repository as a package, so we can run `racket` with the `-l` flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this: - -``` -web: racket -l sample-heroku-app/server -``` - -Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can [navigate to our app's URL and actually see it running live][app-url]! - -# Conclusion - -That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated. - -That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can [find it on GitHub here][app-repo]. For more details on the buildpack itself, [it's also available on GitHub here][buildpack-repo]. - - -[app-repo]: https://github.com/lexi-lambda/racket-sample-heroku-app -[app-url]: https://racket-heroku-sample.herokuapp.com -[buildpack-repo]: https://github.com/lexi-lambda/heroku-buildpack-racket -[heroku]: https://www.heroku.com -[racket-web-server]: http://docs.racket-lang.org/web-server/index.html diff --git a/blog/posts/2015-08-30-managing-application-configuration-with-envy.md b/blog/posts/2015-08-30-managing-application-configuration-with-envy.md deleted file mode 100644 index 8a73f89..0000000 --- a/blog/posts/2015-08-30-managing-application-configuration-with-envy.md +++ /dev/null @@ -1,88 +0,0 @@ - Title: Managing application configuration with Envy - Date: 2015-08-30T16:05:37 - Tags: envy, racket, 12factor - -Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, [The Twelve-Factor App][12factor] provides a set of standards for keeping web apps sane, and [one of those guidelines advises keeping configuration in the environment][12factor-config]. - -[Envy][envy] is the declarative bridge between Racket code and the outside world of the environment. - -# Introducing Envy - -I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require `envy` and you're good to go. - -The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables: - -```racket -; environment.rkt -#lang typed/racket/base - -(require envy) - -(define/provide-environment - api-token - [log-level : Symbol #:default 'info] - [parallel? : Boolean]) -``` - -When this module is required, Envy will automatically do the following: - - 1. Envy will check the values of three environment variables: `API_TOKEN`, `LOG_LEVEL`, and `PARALLEL`. - - 2. If either `API_TOKEN` or `PARALLEL` is not set, an error will be raised: - - ``` - envy: The required environment variable "API_TOKEN" is not defined. - ``` - - 3. The values for `LOG_LEVEL` and `PARALLEL` will be parsed to match their type annotations. - - 4. If `LOG_LEVEL` is not set, it will use the default value, `'info`. - - 5. The values will be stored in `api-token`, `log-level`, and `parallel?`, all of which will be provided by the enclosing module. - -Now just `(require (prefix-in env: "environment.rkt"))`, and the environment variables are guaranteed to be available in your application's code. - -# Working with Typed Racket - -As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, `define/provide-environment` will *only* work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module. - -However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary. - -``` -> parallel? -- : Boolean -#t -``` - -Envy really shines when using optional environment variables with the `#:default` option. The type of the value given to `#:default` doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type. - -``` -> (define-environment - [num-threads : Positive-Integer #:default #f]) -> num-threads -- : (U Positive-Integer #f) -#f -``` - -This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist. - -# And more... - -To see the full set of features that Envy already provides, [take a look at the documentation][envy-docs]. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, [open an issue and make a suggestion][envy-issues]! I already have plans for a `#lang envy` DSL, which will hopefully cut the boilerplate out in its entirety. - -And finally, to give credit where credit is due, Envy is heavily inspired by [Envied][envied] (both in name and function), an environment variable manager for Ruby, which I've used to great effect. - -Try it out! - -- `raco pkg install envy` - -- [Envy on GitHub][envy] - -- [Envy documentation][envy-docs] - -[12factor]: http://12factor.net -[12factor-config]: http://12factor.net/config -[envied]: https://github.com/eval/envied -[envy]: https://github.com/lexi-lambda/envy -[envy-docs]: https://lexi-lambda.github.io/envy/envy.html -[envy-issues]: https://github.com/lexi-lambda/envy/issues diff --git a/blog/posts/2015-09-23-canonical-factories-for-testing-with-factory-girl-api.md b/blog/posts/2015-09-23-canonical-factories-for-testing-with-factory-girl-api.md deleted file mode 100644 index d3753d8..0000000 --- a/blog/posts/2015-09-23-canonical-factories-for-testing-with-factory-girl-api.md +++ /dev/null @@ -1,90 +0,0 @@ - Title: Canonical factories for testing with factory_girl_api - Date: 2015-09-23T16:30:12 - Tags: ruby, rails, javascript, angular - -Modern web applications are often built as *single-page apps*, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests. - -To attempt to address a fraction of this problem, I built [factory_girl_api][factory_girl_api], a way to share context setup between both sides of the application. - -# A brief overview of factory_girl - -In the land of Ruby and Rails, [factory_girl][factory_girl] is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this: - -```ruby -FactoryGirl.define do - factory :widget do - sequence(:name) { |id| 'Widget #' + id } - price 10 - - trait :expensive do - price 1000 - end - end -end -``` - -This makes it easy to create new instances of `Widget` and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units: - -```ruby -widget = FactoryGirl.create :widget -``` - -We can also create more expensive widgets by using the `:expensive` trait. - -```ruby -expensive_widget = FactoryGirl.create :widget, :expensive -``` - -Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually. - -```ruby -fancy_widget = FactoryGirl.create :widget, :expensive, name: 'Fancy Widget' -``` - -It works well, and it keeps initialization boilerplate out of individual tests. - -# Testing on the front-end - -Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually: - -```js -var fancyWidget = new Widget({ - name: 'Fancy Widget', - price: 1000 -}); -``` - -Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a *single, canonical source* for all of our factories. - -## Reusing server-side factories with factory_girl_api - -To help alleviate this problem, I created the [factory_girl_api][factory_girl_api] gem for Rails and the [angular-factory-girl-api][angular-factory-girl-api] Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests. - -The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported: - -```js -FactoryGirl.create('widget', 'expensive', { name: 'Fancy Widget' }); -``` - -In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests. - -## The problems with relying on the server for data - -In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not *completely* convinced it's the right solution yet. - -First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides. - -My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development. - -## Potential improvements and other paths to success - -I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This *kind* of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know. - -Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin! - -- [factory_girl_api][factory_girl_api] -- [angular-factory-girl-api][angular-factory-girl-api] - -[factory_girl]: https://github.com/thoughtbot/factory_girl -[factory_girl_api]: https://github.com/lexi-lambda/factory_girl_api -[angular-factory-girl-api]: https://github.com/lexi-lambda/angular-factory-girl-api diff --git a/blog/posts/2015-11-06-functionally-updating-record-types-in-elm.md b/blog/posts/2015-11-06-functionally-updating-record-types-in-elm.md deleted file mode 100644 index 8dfd888..0000000 --- a/blog/posts/2015-11-06-functionally-updating-record-types-in-elm.md +++ /dev/null @@ -1,233 +0,0 @@ - Title: Functionally updating record types in Elm - Date: 2015-11-06T19:58:40 - Tags: elm - -[Elm][elm-lang] is a wonderful language for building web apps, and I love so much of its approach to language design. Elm does so many things *right* straight out of the box, and that's a real breath of fresh air in the intersection of functional programming and web development. Still, it gets one thing wrong, and unfortunately, that one thing is incredibly important. Elm took the "functions" out of "functional record types". - -Almost any software program, at its core, is all about data. Maybe it's about computing data, maybe it's about manipulating data, or maybe it's about displaying data, but at the end of the day, some sort of data model is going to be needed. The functional model is a breathtakingly elegant system for handling data and shuttling it around throughout a program, and [functional reactive programming][frp], which Elm uses to model event-like interactions, makes this model work even better. The really important thing, though, is what tools Elm actually gives you to model your data. - -# A brief primer on Elm records - -Elm supports all the core datatypes one would expect—numbers, strings, booleans, optionals, etc.—and it allows users to define their own types with ADTs. However, Elm also provides another datatype, which it calls "records". Records are similar to objects in JavaScript: they're effectively key-value mappings. They're cool data structures, and they work well. Here's an example of creating a `Point` datatype in Elm: - -```elm -type alias Point = - { x : Float, y : Float } -``` - -Notice that `Point` is declared as a type *alias*, not as a separate type like an ADT. This is because record types are truly encoded in the type system as values with named fields, not as disparate types. This allows for some fun tricks, but that's outside the scope of this blog post. - -# The good - -What I'd like to discuss is what it looks like to *manipulate* these data structures. Constructing them is completely painless, and reading from them is super simple. This is where the record system gets everything very *right*. - -```elm -origin : Point -origin = { x = 0, y = 0 } - -distanceBetween : Point -> Point -> Float -distanceBetween a b = - let dx = a.x - b.x - dy = a.y - b.y - in sqrt (dx*dx + dy*dy) -``` - -The syntax is clean and simple. Most importantly, however, the record system is functional (in the "functional programming" sense). In a functional system, it's useful to express concepts in terms of function composition, and this is very easy to do in Elm. Creating a function to access a field would normally be clunky if you always needed to do `record.field` to access the value. Fortunately, Elm provides some sugar: - -```elm --- These two expressions are equivalent: -(\record -> record.field) -.field -``` - -Using the `.field` shorthand allows writing some other functions in terms of composition, as most functional programmers would desire: - -```elm -doubledX : Point -> Float -doubledX = ((*) 2) << .x -``` - -This satisfies me. - -# The bad - -So if everything in Elm is so great, what am I complaining about? Well, while the syntax to access fields is convenient, the syntax to *functionally set* fields is questionably clunky. Consider a function that accepts a point and returns a new point with its `x` field set to `0`: - -```elm -zeroedX : Point -> Point -zeroedX point = { point | x <- 0 } -``` - -This doesn't look too bad, does it? It's clear and concise. To me, though, there's something deeply wrong here... this function has a lot of redundancy! It seems to me like we should be able to write this function more clearly in a point-free style. The `.field` shorthand "functionalizes" the record getter syntax, so there must be a function version of the update syntax, right? Maybe it would look something like this: - -```elm -zeroedX : Point -> Point -zeroedX = !x 0 -``` - -But alas, there is no such syntax. - -Now you may ask... why does it matter? This seems trivial, and in fact, the explicit updater syntax may actually be more readable by virtue of how explicit it is. You'd be right, because so far, these examples have been horribly contrived. But let's consider a slightly more useful example: *functionally updating* a record. - -What's the difference? Well, say I wanted to take a point and increment its `x` field by one. Well, I can easily write a function for that: - -```elm -incrementX : Point -> Point -incrementX point = { point | x <- point.x + 1 } -``` - -Not terrible, though a *little* verbose. Still, what if we want to also add a function that *decrements* `x`? - -```elm -decrementX : Point -> Point -decrementX point = { point | x <- point.x - 1 } -``` - -Oh, gosh. That's basically the exact same definition but with the operation flipped. Plus we probably want these operations for `y`, too. Fortunately, there's an easy solution: just pass a function in to *transform* the value! We can define an `updateX` function that allows us to do that easily, then we can define our derived operations in terms of that: - -```elm -updateX : (Float -> Float) -> Point -> Point -updateX f point = { point | x <- f point.x } - -incrementX : Point -> Point -incrementX = updateX ((+) 1) - -decrementX : Point -> Point -decrementX = updateX (\x -> x - 1) -``` - -Not only is that much cleaner, but we can now use it to implement all sorts of other operations that allow us to add, subtract, multiply, or divide the `x` field. Now we just need to generalize our solution to work with the `x` *and* `y` fields! - -Oh, wait. **We can't.** - -# The ugly - -This is where everything breaks down completely. Elm does not offer enough abstraction to reduce this level of crazy duplication: - -```elm -updateX : (Float -> Float) -> Point -> Point -updateX f point = { point | x <- f point.x } - -incrementX : Point -> Point -incrementX = updateX ((+) 1) - -decrementX : Point -> Point -decrementX = updateX (\x -> x - 1) - -updateY : (Float -> Float) -> Point -> Point -updateY f point = { point | y <- f point.y } - -incrementY : Point -> Point -incrementY = updateY ((+) 1) - -decrementY : Point -> Point -decrementY = updateY (\x -> x - 1) -``` - -We sure can give it a shot, though. At the very least, we *can* implement the increment and decrement functions in a more general way by passing in an updater function: - -```elm -increment : ((Float -> Float) -> a -> a) -> a -> a -increment update = update ((+) 1) -``` - -Now, with `updateX` and `updateY`, we can increment either field very clearly and expressively. If we shorten the names to `uX` and `uY`, then the resulting code is actually very readable: - -```elm -pointAbove = uY (\x -> x + 1) -pointBelow = uY (\x -> x - 1) -``` - -It's almost like English now: "update Y using this transformation". This is actually pretty satisfactory. The trouble arises when you have a struct with many fields: - -```elm -type alias PlayerStats = - { health : Integer - , strength : Integer - , charisma : Integer - , intellect : Integer - -- etc. - } -``` - -It might be very convenient to have generic functional updaters in this case. One could imagine a game that has `Potion` items: - -```elm -type Potion = Potion String (PlayerStats -> PlayerStats) -``` - -And then some different kinds of potions: - -```elm -potions = - [ (Potion "Health Potion" (uHealth ((+) 1))), - , (Potion "Greater Intellect Potion" (uIntellect ((+) 3))) - , (Potion "Potion of Weakness" (uStrength (\x -> x // 5))) - ] -``` - -This is a really elegant way to think about items that can affect a player's stats! Unfortunately, it also means you have to define updater functions for *every single field in the record*. This can get tedious rather quickly: - -```elm -uHealth : (Integer -> Integer) -> PlayerStats -> PlayerStats -uHealth f stats = { stats | health <- f stats.health } - -uStrength : (Integer -> Integer) -> PlayerStats -> PlayerStats -uStrength f stats = { stats | strength <- f stats.strength } - -uCharisma : (Integer -> Integer) -> PlayerStats -> PlayerStats -uCharisma f stats = { stats | charisma <- f stats.charisma } - --- etc. -``` - -This is pretty icky. Could there be a better way? - -# Trying to create a more general abstraction - -Interestingly, this pattern doesn't *need* to be this bad. There are better ways to do this. Let's revisit our updater functions. - -Really, `update` can be defined in terms of two other primitive operations: a read and a (functional) write. What would it look like if we implemented it that way instead of requiring special updater functions to be defined? Well, it would look like this: - -```elm -update : (a -> b) -> (b -> a -> a) -> (b -> b) -> a -> a -update get set f x = set (f (get x)) x -``` - -The type definition is a little long, but it's really pretty simple. We just supply a getter and a setter, then a function to do the transformation, and finally a record to actually transform. Of course, as you can see from the type, this function isn't actually specific to records: it can be used with any value for which a getter and setter can be provided. - -The trouble here is that writing field setters isn't any easier in Elm than writing field updaters. They still look pretty verbose: - -```elm -sHealth : Integer -> PlayerStats -> PlayerStats -sHealth x stats = { stats | health <- x } - -uHealth : (Integer -> Integer) -> PlayerStats -> PlayerStats -uHealth = update .health sHealth -``` - -So, at the end of it all, this isn't really a better abstraction. Still remember my fantasy `!field` setter shorthand half a blog post ago? Now perhaps it makes a little more sense. *If* such a syntax existed, then defining the updater would be incredibly simple: - -```elm -uHealth : (Integer -> Integer) -> PlayerStats -> PlayerStats -uHealth = update .health !health -``` - -Still, no syntax, no easy updaters, and by extension, no easy, declarative description of behavior without quite a bit of boilerplate. - -# Conclusions and related work - -Elm is a very promising language, and it seems to be in fairly rapid development. So far, its author, [Evan Czaplicki][czaplic], has taken a very cautious approach to implementing language features, especially potentially redundant ones. This caution is why things like operator slicing, "where" clauses, and special updater syntax have not yet made it into the language. Maybe at some point these will be deemed important enough to include, but for the time being, they've been excluded. - -I obviously think that having this sort of thing is incredibly important to being able to write expressive code without a huge amount of overhead. However, I also do *not* want to give the impression that I think adding special setter syntax is the only way to do it. - -Seasoned functional programmers will surely have noticed that many of these concepts sound a lot like lenses, and Elm actually already has a lens-like library authored by Evan himself, called [Focus][focus]. This, however, does not actually solve the problem: it requires manual description of setters just like the purely function based approach does. Really, lenses are just the logical next step in the line of abstraction I've already laid out above. - -Interestingly, PureScript and Elm, the two Haskell-likes-on-the-frontend that I've paid attention to (though PureScript is much closer to Haskell than Elm), both have this very same problem. Haskell itself solves it with macros via Template Haskell. My favorite language, Racket, solves it with its own macro system. Is there another way to do these things that doesn't involve introducing a heavyweight macro system? Definitely. But I think this is a *necessary feature*, not a "nice to have", so if a macro system is out of the picture, then a simpler, less flexible solution is the obvious logical alternative. - -I really like Elm, and most of my experiences with it have been more than enough to convince me that it is a fantastic language for the job. Unfortunately, the issue of functional record updaters has been quite the frustrating obstacle in my otherwise frictionless ride. I will continue to happily use Elm over other, far less accommodating tools, but I hope that issues like these will be smoothed out as the language and its ecosystem matures. - -[czaplic]: https://twitter.com/czaplic -[elm-lang]: http://elm-lang.org -[focus]: https://github.com/evancz/focus -[frp]: https://en.wikipedia.org/wiki/Functional_reactive_programming diff --git a/blog/posts/2015-12-21-adts-in-typed-racket-with-macros.md b/blog/posts/2015-12-21-adts-in-typed-racket-with-macros.md deleted file mode 100644 index 1f734a4..0000000 --- a/blog/posts/2015-12-21-adts-in-typed-racket-with-macros.md +++ /dev/null @@ -1,321 +0,0 @@ - Title: ADTs in Typed Racket with macros - Date: 2015-12-21T17:57:07 - Tags: racket, typed racket, macros - -Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate *why* macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is **ADTs**. - -# Warning: this is not a macro tutorial - -First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you *are* interested in learning about macros, I must recommend Greg Hendershott's [Fear of Macros][fear-of-macros]. It is good. This is not that. - -Now, with that out of the way, let's get started. - -# What we're building - -[Algebraic data types][adts], or *ADTs*, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at. - -Racket also already has a facility for creating custom data structures in the form of *structs*, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system. - -With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this: - -```haskell -data Tree = Empty - | Leaf Int - | Node Tree Tree -``` - -This already demonstrates a few of the core things we'll need to build: - - 1. Each ADT has a *data type*, in this case `Tree`. This name only exists in the world of types, it isn't a value. - 2. Each ADT has various *data constructors*, in this case `Leaf` and `Node`. - 3. Each data constructor may accept any number of arguments, each of which have a specific type. - 4. The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive. - -Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter: - -```haskell -data Tree a = Empty - | Leaf a - | Node (Tree a) (Tree a) -``` - -With this in mind, we can add a fifth and final point to our list: - - 5. ADTs must be able to be parametrically polymorphic. - -That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket. - -## Describing ADTs in Racket - -How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges: - -```racket -(define-datatype (Tree a) - Empty - (Leaf a) - (Node (Tree a) (Tree a))) -``` - -This looks pretty good. Just like with the Haskell implementation, `Tree` should only exist at the type level, and `Empty`, `Leaf`, and `Node` should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be `(Leaf 7)`. - -Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's `match`[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this: - -```racket -(: tree-sum ((Tree Integer) -> Integer)) -(define (tree-sum tree) - (match tree - [(Empty) 0 ] - [(Leaf n) n ] - [(Node l r) (+ (tree-sum l) - (tree-sum r))])) -``` - -Given that Racket's `struct` form automatically produces identifiers that cooperate with `match`, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation. - -# Implementing ADTs as syntax - -Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, [`syntax/parse`][syntax-parse]. The `syntax/parse` library works like the traditional Scheme `syntax-case` on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components. - -Since this is not a macro tutorial, the following implementation assumes you already know how to use `syntax/parse`. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play. - -## Parsing types with a syntax class - -To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like `Tree`, or they can be identifiers with parameters, like `(Tree a)`. We'll want to cover both cases. - -```racket -(begin-for-syntax - (define-syntax-class type - (pattern name:id #:attr [param 1] '()) - (pattern (name:id param ...+)))) -``` - -This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (`...+`) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use `#:attr` to give the `param` attribute the default value of an empty list, so this syntax class will actually *normalize* the input we get in addition to actually parsing it. - -## A first attempt at `define-datatype` - -Now we can move on to actually implementing `define-datatype`. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using `syntax-parser`, which actually does the parsing for our macro. - -```racket -(define-syntax define-datatype - (syntax-parser - [(_ type-name:type data-constructor:type ...) - ])) -``` - -This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with `_` (which will just be the identifier `define-datatype`), then expecting a `type-name`, which uses the `type` syntax class we defined above. Next, we expect zero or more `data-constructor`s, which also use the `type` syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro. - -Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this: - -```racket -(define-syntax define-datatype - (syntax-parser - [(_ type-name:type data-constructor:type ...) - #'(begin - (struct data-constructor.name ([f : data-constructor.param] ...) - ...))])) -``` - -This is actually really close to being correct. This will generate a struct definition for each `data-constructor`, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have *names*, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for *all* the fields, `f`, so if any data constructor has two or more fields, we'll get an error. - -Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called `generate-temporary`, which generates random identifiers. Our next attempt might look like this: - -```racket -#`(begin - (struct data-constructor.name - ([#,(generate-temporary) : data-constructor.param] ...) - ...)) -``` - -The `#,` lets us "escape" from the template to execute `(generate-temporary)` and interpolate its result into the syntax. Unfortunately, this doesn't work. We *do* generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type. - -## More leveraging syntax classes - -As it turns out, this is *also* easy to do with syntax classes. We can add an extra attribute to our `type` syntax class to generate a random identifier with each one. Again, we can use `#:attr` to do that automatically. Our new definition for `type` will look like this: - -```racket -(begin-for-syntax - (define-syntax-class type - (pattern name:id - #:attr [param 1] '() - #:attr [field-id 1] '()) - (pattern (name:id param ...+) - #:attr [field-id 1] (generate-temporaries #'(param ...))))) -``` - -Here we're using `generate-temporaries` instead of `generate-temporary`, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each `param`. - -We can now fix our macro to use this `field-id` attribute instead of the static field name: - -```racket -#'(begin - (struct data-constructor.name - ([data-constructor.field-id : data-constructor.param] ...)) - ...) -``` - -## Creating the supertype - -We're almost done—now we just need to implement our overall type, the one defined by `type-name`. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this: - -```racket -(define-type Tree (U Empty Leaf Node)) -``` - -However, a polymorphic type alias would need to include the type parameters in each subtype, like this: - -```racket -(define-type (Tree a) (U (Empty a) (Leaf a) (Node a))) -``` - -How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using **procedural macros**. - -To build each properly-instantiated type, we'll use a combination of `define/with-syntax` and Racket's list comprehensions, `for/list`. The `define/with-syntax` form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by `syntax-parser`. This will allow us to break up our result into multiple steps. Technically, `define/with-syntax` is not strictly necessary—we could just use `` #` `` and `#,`—but it's cleaner to work with. - -We'll start by defining a set of instantiated data constructor types, one per `data-constructor`: - -```racket -(define/with-syntax [data-type ...] - (for/list ([name (in-syntax #'(data-constructor.name ...))]) - )) -``` - -Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need: - -```racket -(define/with-syntax [data-type ...] - (for/list ([name (in-syntax #'(data-constructor.name ...))]) - (if (stx-null? #'(type-name.param ...)) - name - #`(#,name type-name.param ...)))) -``` - -Now with our definition for `data-type`, we can implement our type alias for the supertype extremely easily: - -```racket -#'(define-type type-name (U data-type ...)) -``` - -## Putting it all together - -There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by `type-name` are in scope for each data constructor's structure definition. We can do this by making use of `type-name.param` within each produced struct definition, resulting in this: - -```racket -#'(begin - (struct data-constructor.name (type-name.param ...) - ([data-constructor.field-id : data-constructor.param] ...)) - ...) -``` - -And we're done! The final macro, now completed, looks like this: - -```racket -(begin-for-syntax - (define-syntax-class type - (pattern name:id - #:attr [param 1] '() - #:attr [field-id 1] '()) - (pattern (name:id param ...+) - #:attr [field-id 1] (generate-temporaries #'(param ...))))) - -(define-syntax define-datatype - (syntax-parser - [(_ type-name:type data-constructor:type ...) - - (define/with-syntax [data-type ...] - (for/list ([name (in-syntax #'(data-constructor.name ...))]) - (if (stx-null? #'(type-name.param ...)) - name - #`(#,name type-name.param ...)))) - - #'(begin - (struct (type-name.param ...) data-constructor.name - ([data-constructor.field-id : data-constructor.param] ...)) ... - (define-type type-name (U data-type ...)))])) -``` - -It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use. - -# Using our ADTs - -With the macro built, we can now actually use our ADTs using the syntax we described! The following is now *valid code*: - -```racket -(define-datatype (Tree a) - Empty - (Leaf a) - (Node (Tree a) (Tree a))) - -> (Node (Leaf 3) (Node (Empty) (Leaf 7))) -- : (Node Positive-Byte) -(Node (Leaf 3) (Node (Empty) (Leaf 7))) -``` - -We can use this to define common data types, such as Haskell's `Maybe`: - -```racket -(define-datatype (Maybe a) - (Just a) - Nothing) - -(: maybe-default (All [a] (Maybe a) a -> a)) -(define (maybe-default m v) - (match m - [(Just a) a] - [(Nothing) v])) - -(: maybe-then (All [a] (Maybe a) (a -> (Maybe a)) -> (Maybe a))) -(define (maybe-then m f) - (match m - [(Just a) (f a)] - [(Nothing) (Nothing)])) -``` - -And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter: - -```racket -(define-datatype Expr - (Value Number) - (Add Expr Expr) - (Subtract Expr Expr) - (Multiply Expr Expr) - (Divide Expr Expr)) - -(: evaluate (Expr -> Number)) -(define (evaluate e) - (match e - [(Value x) x ] - [(Add a b) (+ (evaluate a) (evaluate b))] - [(Subtract a b) (- (evaluate a) (evaluate b))] - [(Multiply a b) (* (evaluate a) (evaluate b))] - [(Divide a b) (/ (evaluate a) (evaluate b))])) - -> (evaluate (Add (Value 1) - (Multiply (Divide (Value 1) (Value 2)) - (Value 7)))) -4 1/2 -``` - -There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, [I've put together a gist here][adts-gist]. - -# Conclusions and credit - -This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly *are* powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp. - -This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here. - -That said, I think it's pretty cool. - -Finally, I must give credit where credit is due. Thanks to [Andrew M. Kent][andmkent] for the creation of the [datatype][racket-datatype] package, which served as the inspiration for this blog post. Many thanks to [Sam Tobin-Hochstadt][samth] for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to [Ryan Culpepper][ryanc] and [Matthias Felleisen][matthias] for their work on creating `syntax/parse`, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to [Matthew Flatt][mflatt] for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later. - -Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling. - -[adts]: https://en.wikipedia.org/wiki/Algebraic_data_type -[adts-gist]: https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e -[andmkent]: http://andmkent.com -[fear-of-macros]: http://www.greghendershott.com/fear-of-macros/ -[matthias]: http://www.ccs.neu.edu/home/matthias/ -[mflatt]: http://www.cs.utah.edu/~mflatt/ -[racket-datatype]: https://github.com/andmkent/datatype -[ryanc]: http://www.ccs.neu.edu/home/ryanc/ -[samth]: http://www.ccs.neu.edu/home/samth/ -[syntax-parse]: http://docs.racket-lang.org/syntax/stxparse.html diff --git a/blog/posts/2016-02-18-simple-safe-multimethods-in-racket.md b/blog/posts/2016-02-18-simple-safe-multimethods-in-racket.md deleted file mode 100644 index a3a5431..0000000 --- a/blog/posts/2016-02-18-simple-safe-multimethods-in-racket.md +++ /dev/null @@ -1,277 +0,0 @@ - Title: Simple, safe multimethods in Racket - Date: 2016-02-18T18:48:32 - Tags: racket, macros - -Racket ships with `racket/generic`, a system for defining *generic methods*, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports *single dispatch*. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible. - -# Motivating multiple dispatch - -What is multiple dispatch and why is it necessary? Well, in most cases, it *isn’t* necessary at all. [It has been shown that multiple dispatch is much rarer than single dispatch in practice.][multiple-dispatch-in-practice] However, when actually needed, having multiple dispatch in the toolbox is a valuable asset. - -A classic example of multiple dispatch is multiplication over both scalars and vectors. Ideally, all of the following operations should work: - -``` -2 × 3 = 6 -2 × ⟨3, 4⟩ = ⟨6, 8⟩ -⟨3, 4⟩ × 2 = ⟨6, 8⟩ -``` - -In practice, most languages do not support such flexible dispatch rules without fairly complicated branching constructs to handle each permutation of input types. Furthermore, since most languages only support single dispatch (such as most object-oriented languages), it is nearly impossible to add support for a new combination of types to an existing method. - -To illustrate the above, even if a language supported operator overloading *and* it included a `Vector` class that overloaded multiplication to properly work with numbers and vectors, it might not implement matrix multiplication. If a user defines a `Matrix` class, they may overload *its* multiplication to support numbers, vectors, and matrices, but it is impossible to extend the multiplication implementation for the `Vector` class. That method is now completely set in stone, unless it is edited directly (and the programmer may not have access to `Vector`’s implementation). - -Multiple dispatch solves all of these problems. Rather than specify implementations of functions for singular types, it is possible to specify implementations for sets of types. In the above example, a programmer would be able to define a new function that operates on `Vector` and `Matrix` arguments. Since each definition does not “belong” to any given type, extending this set of operations is trivial. - -# Multiple dispatch in Racket - -This blog post is somewhat long and technical, so before proceeding any further, I want to show some real code that actually works so you can get a feel for what I’m talking about. As a proof-of-concept, I have created [a very simple implementation of multiple dispatch in Racket][racket-multimethod]. The above example would look like this in Racket using my module: - -```racket -#lang racket - -(require multimethod) - -(provide mul - (struct-out num) - (struct-out vec)) - -(struct num (val)) -(struct vec (vals)) - -(define-generic (mul a b)) - -(define-instance ((mul num num) x y) - (num (* (num-val x) (num-val y)))) - -(define-instance ((mul num vec) n v) - (vec (map (curry * (num-val n)) (vec-vals v)))) - -(define-instance ((mul vec num) v n) - (mul n v)) -``` - -Pardon the somewhat clunky syntax, but the functionality is there. Using the above code works as expected: - -``` -> (mul (num 2) (num 3)) -(num 6) -> (mul (num 2) (vec '(3 4))) -(vec '(6 8)) -> (mul (vec '(3 4)) (num 2)) -(vec '(6 8)) -``` - -Making the above snippet work is not particularly hard. In fact, it’s likely that most competent Racketeers could do it without much thought. However, there’s a tiny bit more going on behind the scenes than it may seem. - -# The problem with multiple dispatch - -The single-dispatch design limitation of `racket/generic` comes directly from a desire to avoid what has been described as “spooky action at a distance”, a problem that is prevalent in many systems that support methods with multiple dispatch (aka *multimethods*). Specifically, the issue arises when new method implementations are defined for existing datatypes, which can have far-reaching effects throughout a program because the method table is global state. Both CLOS and Clojure suffer from this shortcoming. - -Interestingly, Haskell with multi-parameter typeclasses (a nonstandard but highly useful extension) makes it quite trivial to create constructs similar to multiple dispatch (though the overload resolution is done at compile-time). The similarities are significant: Haskell *also* suffers from the possibility of a certain sort of “spooky action”. However, Haskell’s static typing and resolution allows the compiler to catch these potential issues, known as “orphan instances”, at compile time. Even though Racket does not support the same sort of static typing, the same idea can be used to keep multiple dispatch safe using the macro system. - -# Safe, dynamically-typed multiple dispatch - -In order to make multiple dispatch safe, we first need to determine exactly what is unsafe. Haskell has rules for determining what constitutes an “orphan instance”, and these rules are equally applicable for determining dangerous multimethod implementations. Specifically, a definition can be considered unsafe if *both* of the following conditions are true: - - 1. The multimethod that is being implemented was declared in a different module from the implementation. - 2. *All* of the types used for dispatch in the multimethod instance were declared in a different module from the implementation. - -Conversely, a multimethod implementation is safe if *either* of the following conditions are true: - - 1. The multimethod that is being implemented is declared in the same module as the implementation. - 2. *Any* of the types used for dispatch in the multimethod instance are declared in the same module as the implementation. - -Why do these two rules provide a strong enough guarantee to eliminate the dangers created by global state? Well, to understand that, we need to understand what can go wrong if these rules are ignored. - -## Multimethods and dangerous instances - -What exactly is this dangerous-sounding “spooky action”, and what causes it? Well, the trouble stems from the side-effectful nature of multimethod instance definitions. Consider the Racket module from earlier, which defines multiplication instances for scalars and vectors: - -```racket -(provide mul - (struct-out num) - (struct-out vec)) - -(struct num (val)) -(struct vec (vals)) - -(define-generic (mul a b)) - -(define-instance ((mul num num) x y) - (num (* (num-val x) (num-val y)))) - -(define-instance ((mul num vec) n v) - (vec (map (curry * (num-val n)) (vec-vals v)))) - -(define-instance ((mul vec num) v n) - (mul n v)) -``` - -Note that there is not actually a `(mul vec vec)` implementation. This is intentional: there are *two* ways to take the product of two vectors, so no default implementation is provided. However, it is possible that another module might desire an instance for `mul` that takes the dot product, and the programmer might write the following definition: - -```racket -(define-instance ((mul vec vec) x y) - (num (foldl + 0 (map * (vec-vals x) (vec-vals y))))) -``` - -However, there is something fishy about the above definition: it doesn’t need to be exported with `provide` to work! Since instances don’t create new bindings, they only add dispatch options, they don’t ever need to `provide` anything. This is problematic, though: it means that a program could continue to happily compile *even if* the module containing the dot product instance was never loaded with `require`, but an attempt to multiply two vectors would fail at runtime, claiming that there was no `(mul vec vec)` implementation. This drastic change of behavior violates Racket programmers’ assumptions about the guarantees made by modules (`require` should not cause any side-effects if the module’s bindings are not used). - -Of course, while this seems potentially unexpected, it is workable: just be careful to `require` modules containing instances. Unfortunately, it gets much worse—what if a different library defines *its own* `(mul vec vec)` instance? What if that instance takes the cross product instead? That library may function entirely properly on its own, but when loaded alongside the program that defines a dot product instance, it is impossible to determine which instance should be used where. Because `define-instance` operates by modifying the aforementioned global state, the implementations clash, and the two systems *cannot* continue to operate together as written. - -This is pretty bad. Defining extra instances is a reasonable use-case for multiple dispatch, but if these instances can break *third-party code*, how can they be trusted? This sort of problem can make multiple dispatch difficult to reason about and even more difficult to trust. - -## What determines safety? - -With those problems in mind, we can turn back to the two rules for *safe* multiple dispatch. How do they prevent the above issues? Well, let’s take them one at a time. - -Remember that an instance can be unequivocally determined to be safe if either of the two conditions are true, so we can consider them entirely independently. The first one is simple—an instance is safe if the following condition holds: - -> The multimethod that is being implemented is declared in the same module as the implementation. - -This one is pretty obvious. It is impossible to create a “bad” instance of a method declared in the same module because it is impossible to import the method without also bringing in the instance. Furthermore, a conflicting instance cannot be defined at the place where the types themselves are defined because that would require a circular module dependency, which Racket does not permit. - -With the above explanation in mind, the second condition should make sense, too: - -> *Any* of the types used for dispatch in the multimethod instance are declared in the same module as the implementation. - -The same argument for the first point holds for the second, but with the parties swapped. Again, it is impossible to use the instance without somehow requiring the module that defines the datatype itself, so the instance would always be required, anyway. The most interesting aspect of this condition is that it demonstrates that instances can be defined for existing datatypes (that are defined in other modules) just so long as *at least one* of the datatypes is defined in the same module. This continues to permit the important use-case of extending the interfaces of existing types. - -## Encoding the safety rules into Racket’s macro system - -In order to keep track of which methods and instances are defined where, I leveraged a technique based on the one [used by Typed Racket to keep track of whether or not a typed identifier is used in a typed or untyped context][typed-racket-scheme2007]. However, instead of using a simple mutable boolean flag, I used a mutable [free identifier set][free-id-set], which keeps track of the identifiers within a given module that should be considered “privileged”. - -```racket -#lang racket/base - -(require syntax/id-set) - -(provide mark-id-as-privileged! - id-privileged?) - -(define privileged-ids (mutable-free-id-set)) - -(define (mark-id-as-privileged! id) - (free-id-set-add! privileged-ids id)) - -(define (id-privileged? id) - (free-id-set-member? privileged-ids id)) -``` - -Making this work with `define-generic` is obvious: just invoke `mark-id-as-privileged!` on the method name to note that the method is “privileged” in the scope of the current module. Keeping track of privileged structs is similarly straightforward, though it is a little more devious: the `multimethod` module provides a custom `struct` macro that just expands to `struct` from `racket/base`, but adds privilege information. - -The `define-instance` macro does all the heavy lifting to ensure that only privileged identifiers can be used in instance definitions. A simple check for the identifier annotations is performed before proceeding with macro expansion: - -```racket -(unless (or privileged? (ormap id-privileged? types)) - (assert-privileged-struct! (first types))) -``` - -When the privilege checks fail, an error is raised: - -```racket -(define (assert-privileged-struct! id) - (unless (id-privileged? id) - (raise-syntax-error 'define-instance - "expected name of struct defined in current module" - id))) -``` - -With the above safeguards in place, the dangerous dot product implementation from above **would not be allowed**. The checks manage to encode both of the safety rules into the macro system such that invalid instances will fail *at compile time*, preventing dangerous uses of multimethods from ever slipping by unnoticed. - -## Actually implementing multiple dispatch - -The rest of the multimethod implementation is relatively straightforward and is not even particularly robust. If anything, it is the bare minimum of what would be needed to allow the safety mechanisms above to work. Lots of features that would likely be needed in a real implementation are not included, and graceful error handling is largely ignored. - -Multimethods themselves are implemented as Racket [transformer bindings][transformer-binding] containing custom data, including a reference to the multimethod’s arity and dispatch table. The custom datatype includes a `prop:procedure` structure type property, which allows such bindings to also function as macros. The macro procedure expands to an operation that looks up the proper instance to use in the multimethod’s dispatch table and invokes it with the supplied arguments. - -The relevant code for defining multimethods is reproduced below: - -```racket -(begin-for-syntax - (struct multimethod (arity dispatch-table) - #:property prop:procedure - (λ (method stx) - (syntax-parse stx - [(method arg ...) - #'(apply-multimethod method (list arg ...))] - [method - #'(λ args (apply-multimethod method args))])))) - -(define-syntax define-generic - (syntax-parser - [(_ (method:id arg:id ...+)) - (with-syntax ([arity (length (attribute arg))] - [dispatch-table (generate-temporary #'method)]) - (mark-id-as-privileged! #'method) - #'(begin - (define dispatch-table (make-hash)) - (define-syntax method (multimethod arity #'dispatch-table))))])) -``` - -The dispatch tables are implemented entirely in terms of Racket’s structure types, so while they can be defined on arbitrary structure types (including ones defined in the Racket standard library), they *cannot* be defined on primitives such as pairs or vectors. Implementations are registered in the dispatch table using the compile-time information associated with structs’ transformer bindings, and the same information is retrieved from struct instances at runtime to look up the proper implementation to call. Notably, this only works if the struct is `#:transparent`, or more generally and accurately, if the calling code has access to the struct’s inspector. All structs defined by the `struct` form from the `multimethod` module are automatically marked as `#:transparent`. - -The following code implements defining multimethod instances: - -```racket -(begin-for-syntax - (define (assert-privileged-struct! id) - (unless (id-privileged? id) - (raise-syntax-error 'define-instance - "expected name of struct defined in current module" - id)))) - -(define-syntax define-instance - (syntax-parser - ; standard (define (proc ...) ...) shorthand - [(_ ((method type:id ...+) . args) body:expr ...+) - #'(define-instance (method type ...) (λ args body ...))] - ; full (define proc lambda-expr) notation - [(_ (method type:id ...+) proc:expr) - (let* ([multimethod (syntax-local-value #'method)] - [privileged? (id-privileged? #'method)]) - (unless (or privileged? (ormap id-privileged? (attribute type))) - (assert-privileged-struct! (first (attribute type)))) - (with-syntax ([dispatch-table (multimethod-dispatch-table multimethod)] - [(struct-type-id ...) (map (compose1 first extract-struct-info syntax-local-value) - (attribute type))]) - #'(let ([struct-types (list struct-type-id ...)]) - (hash-set! dispatch-table struct-types proc))))])) -``` - -The resulting implementation is a useful, if certainly incomplete implementation of multimethods in Racket that does not sacrifice the safety provided by `racket/generic`’s single-dispatch approach. - -# Related work, advantages and disadvantages, and areas for future improvement - -As previously mentioned, this implementation of multiple dispatch was inspired by the types of APIs offered by CLOS and Clojure while also maintaining the safety of `racket/generic`. The inspiration for the safety rules came from GHC’s detection of orphan instances. Although most of the ideas presented above exist in other places, I am unsure if the concept of safety checking has been used before in any dynamically-typed programming languages. - -The primary advantage offered over Racket’s existing generics system is obvious: multiple dispatch. Furthermore, this system can supersede many uses of `racket/generic` simply by dispatching on a single type. However, the current implementation does *not* support all of the features of `racket/generic`, such as supporting non-structure types and allowing fallback implementations. While those are well within the realm of possibility, other things like attaching structure type properties are probably not possible with this approach, so it is unlikely that the existing system could be subsumed by one like this one. - -Additionally, this implementation would almost certainly need numerous improvements before being useful to most programmers: - - - **Good error reporting for failure cases.** Right now, even something obvious like calling a method on values that do not implement it simply fails with an error produced by `hash-ref`. In a more interesting sense, using the arity to generate compile-time error messages for `define-instance` would be a nice improvement. - - - **Support for Racket primitive data types.** This might require some cooperation from Racket itself to permit an elegant implementation, but they could also just be special-cased. So long as lookup for primitives was done *after* consulting the main dispatch table, there wouldn’t be any performance hit for non-primitive types. - - - **Option to supply fallback implementations.** This wouldn’t be too hard at all, though it’s questionable whether or not it would be useful without method groupings like `define/generic` provides. There would likely also need to be some sort of way to check if a set of values implements a particular method. - - - **Better cooperation with structure inspectors to alleviate the need for all structures to be transparent.** It’s currently unclear to me how exactly this works and how it *should* work. There might be a better way to do this without mucking with inspectors. - - - **Much more flexible argument lists, including the ability to specify arguments that are not used for dispatch.** This is really a pretty fundamental requirement, but the parsing required was significant enough for me to put it off for this initial prototype. - - - **Scribble forms to document generic methods and their instances.** This is something `racket/generic` *doesn’t* have, and it has suffered for it. It would be very nice to have easy documentation forms for multimethods. - - - **Proper consideration of struct subtyping.** Racket structs support subtyping, which I have not given much thought for this prototype. It is possible that subtyping violates constraints I had assumed would hold, so reviewing the existing code with that context would be useful. - -I’m not sure how much effort is involved in most of the above ideas, and in fact I’m not even completely sure how useful this system is to begin with. I have not found myself reaching much for multiple dispatch in my time as a Racket programmer, but that could simply be because it was previously unavailable. It will be interesting to see if that changes now that I have built this system, even if it is a bit rough around the edges. - -# Conclusion - -Despite the lack of need for multiple dispatch to solve most problems, as indicated by its general lack of support in mainstream programming languages, it’s a nice tool to have in the toolbox, and it *is* asked for in the Racket community from time to time (perhaps due to its familiarity in other parts of the Lisp world). Time will tell if pointing people to something like this will create or stifle interest in multiple dispatch for Racket. - -The source for the [`multimethod` package can be found here][racket-multimethod] if you are at all interested in playing with it yourself. - -[free-id-set]: http://docs.racket-lang.org/syntax/syntax-helpers.html#%28tech._identifier._set%29 -[multiple-dispatch-in-practice]: http://dl.acm.org/citation.cfm?doid=1449764.1449808 -[racket-multimethod]: https://github.com/lexi-lambda/racket-multimethod -[transformer-binding]: http://docs.racket-lang.org/guide/proc-macros.html#%28tech._transformer._binding%29 -[typed-racket-scheme2007]: http://www.ccs.neu.edu/racket/pubs/scheme2007-ctf.pdf diff --git a/blog/posts/2016-06-03-four-months-with-haskell.md b/blog/posts/2016-06-03-four-months-with-haskell.md deleted file mode 100644 index f6d14b3..0000000 --- a/blog/posts/2016-06-03-four-months-with-haskell.md +++ /dev/null @@ -1,439 +0,0 @@ - Title: Four months with Haskell - Date: 2016-06-12T16:02:27 - Tags: haskell, functional programming - -At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it. - -Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an *incredible* programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code. - -# Dispelling some myths - -Before moving on and discussing my experiences in depth, I want to take a quick detour to dispel some frequent rumors I hear about why Haskell is at least potentially problematic. These are things I hear a *lot*, and nothing in my experience so far would lead me to believe these are actually true. Ultimately, I don’t want to spend too much time on these—I think that, for the most part, they are nitpicks that people complain about to avoid understanding the deeper and more insidious problems with the language—but I think it’s important to at least mention them. - -## Hiring Haskell developers is not hard - -I am on the first Haskell team in my company, and I am among the first Haskell developers we ever hired. Not only were we hiring without much experience with Haskell at all, we explicitly *did not* want to hire remote. Debate all you like about whether or not permitting remote work is a good idea, but I don’t think anyone would dispute that this constraint makes hiring much harder. We didn’t have any trouble finding a very large stream of qualified applicants, and it definitely seems to have dispelled any fears that we would have trouble finding new candidates in the future. - -## Performing I/O in Haskell is easy - -Haskell’s purity is a point of real contention, and it’s one of the most frustrating complaints I often hear about Haskell. It is surprisingly common to hear concerns along the lines of “I don’t want to use Haskell because its academic devotion to purity sounds like it would make it very hard to get anything done”. There are very valid reasons to avoid Haskell, but in practice, I/O is not one of them. In fact, I found that isolating I/O in Haskell was much the same as isolating I/O in every other language, which I need to do anyway to permit unit testing. - -...you *do* write deterministic unit tests for your impure logic, right? - -## Working with lots of monads is not very difficult - -The “M word” has ended up being a running joke *about* Haskell that actually ends up coming up fairly rarely *within* the Haskell community. To be clear, there is *no doubt* in my mind that monads make Haskell intimidating and provide a steep learning curve for new users. The proliferation of the joke that monads are impossible to explain, to the point of becoming mythologized, is absolutely indicative of a deeper problem about Haskell’s accessibility. However, once people learn the basics about monads, I’ve found that applying them is just as natural as applying any other programming pattern. - -Monads are used to assist the programmer, not impede them, and they really do pay off in practice. When something has a monadic interface, there’s a decent chance I already know what that interface is going to do, and that makes working with lots of different monads surprisingly easy. Admittedly, I do rely very, very heavily on tooling to help me out here, but with things like mouseover type tooltips, I’ve actually found that working with a variety of different monads and monad transformers is actually quite pleasant, and it makes things very readable! - -# Haskell: the good parts - -With the disclaimers out of the way, I really just want to gush for a little bit. This is not going to be an objective, reasoned survey of why Haskell is good. I am not even really going to touch upon why types are so great and why purity is so wonderful—I’d love to discuss those in depth, but that’s for a different blog post. For now, I just want to touch upon the real surprises, the real things that made me *excited* about Haskell in ways I didn’t expect. These are the things that my subjective little experience has found fun. - -## Language extensions *are* Haskell - -There was a time in my life when I spent a lot of time writing C. There are a lot of compilers for C, and they all implement the language in subtly different but often incompatible ways, especially on different platforms. The only way to maintain a modicum of predictability was to adhere to the standards *religiously*, even when certain GCC or MSVC extensions seem tantalizingly useful. I was actually bitten a few times by real instances where I figured I’d just use a harmless extension that was implemented everywhere, then found out it worked slightly differently across different compilers in a particular edge case. It was a learning experience. - -It seems that this fear provides a very real distrust for using GHC’s numerous *language extensions*, and indeed, for a long time, I felt that it was probably an admirable goal to stick to Haskell 98 or Haskell 2010 as closely as possible. Sometimes I chose a slightly more verbose solution that was standard Haskell to avoid turning on a trivial extension that would make the code look a little bit cleaner. - -About a year later, I’m finding that attitude was not only a mistake, but it forced me to often completely miss out on a lot of Haskell’s core value. GHC *won*, and now GHC and Haskell are basically synonymous. With that in mind, the portability concerns of language extensions are a bit of a non-issue, and turning them on is a very good idea! Some extensions are more than a little dangerous, so they cannot all be turned on without thinking, but the question is absolutely not “Is using language extensions a good idea?” and more “Is using *this* language extension a good idea?” - -This is important, and I bring it up for a reason: so much of the awesomeness of Haskell is locked behind language extensions. Turning a lot of these on is one of the main things that made me really start to see how incredibly powerful Haskell actually is. - -## Phantom types - -I’m going to start out by talking about *phantom types*, which are a pretty simple concept but a powerful one, and they serve as the foundation for a lot of other cool type-level tricks that can make Haskell extremely interesting. The basic idea of a phantom type is simple; it’s a type parameter that isn’t actually used to represent any particular runtime value: - -```haskell -newtype Id a = Id Text -``` - -This type represents an id for some kind of value, but although the kind of value is specified in the type as the `a` type parameter, it isn’t actually used anywhere on the data definition—no matter what `a` is, an `Id` is just a piece of text. This makes it possible to write functions that operate on specific kinds of ids, and those invariants will be statically checked by the compiler, even though the runtime representation is entirely identical: - -```haskell -fetchUser :: MonadDB m => Id User -> m User -``` - -Using `FlexibleInstances`, it’s also possible to create different instances for different kinds of ids. For example, it would be possible to have different `Show` instances depending on the type of id in question. - -```haskell -instance Show (Id User) where - show (Id txt) = "user #" <> unpack txt - -instance Show (Id Post) where - show (Id txt) = "post #" <> unpack txt -``` - -This provides a simple framework for encoding entirely arbitrary information into the type system, then asking the compiler to actually check assertions about that information. This is made even more powerful with some other extensions, which I’ll talk about shortly. - -## Letting the compiler write code - -One of the things I really dislike, more than most things, is boilerplate. A little bit of boilerplate is fine—even necessary at times—but as soon as I start wondering if a code generator would improve things, I think the programming language has pretty much failed me. - -I write a lot of Racket because, in a sense, Racket is the ultimate boilerplate killer: the macro system is a first-class code generator integrated with the rest of the language, and it means that boilerplate is almost never an issue. Of course, that’s not always true: sometimes a bit of boilerplate *is* still necessary because macros cannot deduce enough information about the program to generate the code entirely on their own, and in Haskell, some of that information is actually present in the type system. - -This leads to two absolutely incredible extensions, both of which are simple and related, but which actually *completely change* how I approach problems when programming. These two extensions are `GeneralizedNewtypeDeriving` and `StandaloneDeriving`. - -### Newtypes and type safety - -The basic idea is that “newtypes” are just simple wrapper types in Haskell. This turns out to be extremely important when trying to find the value of Haskell because they allow you to harden type safety by specializing types to *your* domain. For example, consider a type representing a user’s name: - -```haskell -newtype Name = Name Text -``` - -This type is extremely simple, and in fact isn’t even at all different from a simple `Text` value with respect to its representation, since all combinations of unicode characters are allowed in a name. Therefore, what’s the point of a separate type? Well, this allows Haskell to introduce actual compilation failures when two different kinds of textual data are mixed. This is not a new idea, and even in languages that don’t support this sort of thing, Joel Spolsky’s old blog post [Making Wrong Code Look Wrong][spolsky-wrong] describes how it can be done by convention. Still, almost every modern language makes this possible: in C, it would be a single-member `struct`, in class-based OO languages, it would be a single-member class... this is not a complicated idea. - -The difference lies in its usage. In other languages, this strategy is actually not very frequently employed for the simple reason that it is almost always extremely annoying. You are forced to do tons of wrapping/unwrapping, and at that point it isn’t really clear if you’re even getting all that much value out of the distinction when your first solution to a type mismatch is wrapping or unwrapping the value without a second thought. In Haskell, however, this can be heavily mitigated by asking the compiler to *automatically derive typeclass implementations*, which allow the unwrapping/wrapping to effectively happen implicitly for a constrained set of operations. - -### Using `GeneralizedNewtypeDeriving` - -Consider the `Name` type once again, but this time, let’s derive a class: - -```haskell -newtype Name = Name Text - deriving (IsString) -``` - -The `IsString` typeclass in Haskell allows custom types to automatically be created from string literals. It is *not* handled specially by Haskell’s `deriving` mechanism. Since `Text` implements `IsString`, an instance will be generated that simply defers to the underlying type, automatically generating the code to wrap the result up in a `Name` box at the end. This means that code like this will now just magically work: - -```haskell -name :: Name -name = "Alyssa P. Hacker" -``` - -No boilerplate needs to be written! This is a neat trick, but it actually turns out to be far more useful than that simple example in practice. What really makes this functionality shine is when you want to derive *some* kinds of functionality but disallow some others. For example, using the [`text-conversions`][text-conversions] package, it’s possible to do something like this: - -```haskell -newtype Id a = Id Text - deriving (Eq, Show, ToText, ToJSON) -``` - -This creates an opaque `Id` type, but it automatically generates conversions *to* textual formats. However, it does *not* automatically create `FromText` or `FromJSON` instances, which would be dangerous because decoding `Id`s can potentially fail. It’s then possible to write out those instances manually to preserve a type safety: - -```haskell -instance FromText (Maybe (Id a)) where - fromText str = if isValidId str then Just (Id str) else Nothing - -instance FromJSON (Id a) where - parseJSON (String val) = maybe (fail "invalid id") return (fromText val) - parseJSON _ = fail "invalid id" -``` - -### Using `StandaloneDeriving` - -The ordinary `deriving` mechanism is extremely useful, especially when paired with the above, but sometimes it is desirable to have a little bit more flexibility. In these cases, `StandaloneDeriving` can help. - -Take the `Id` example again: it has a phantom type, and simply adding something like `deriving (ToText)` with derive `ToText` instances for *all* kinds of ids. It is potentially useful, however, to derive instances for more specific id types. Using standalone `deriving` constructs permits this sort of flexibility. - -```haskell -deriving instance ToText (Id User) - -instance ToText (Id Post) where - toText = postIdToText -``` - -This is an example where GHC language extensions end up becoming significantly more than the sum of their parts, which seems to be a fairly frequent realization. The `StandaloneDeriving` mechanism is a little bit useful without `GeneralizedNewtypeDeriving`, but when combined, they are incredibly powerful tools for getting a very fine-grained kind of type safety *without* writing any boilerplate. - -## DataKinds are super cool, with caveats - -Phantom types are quite wonderful, but they can only encode *types*, not arbitrary data. That’s where `DataKinds` and `KindSignatures` come in: they allow lifting arbitrary datatypes to the type level so that things that would normally be purely runtime values can be used at compile-time as well. - -The way this works is pretty simple—when you define a datatype, you also define a “datakind”: - -```haskell -data RegistrationStatus = Registered | Anonymous -``` - -Normally, the above declaration declares a *type*, `RegistrationStatus`, and two *data constructors*, `Registered` and `Anonymous`. With `DataKinds`, it also defines a *kind*, `RegistrationStatus`, and two *type constructors*, `Registered` and `Anonymous.` - -If that’s confusing, the way to understand that is to realize there is a sort of natural ordering here: types describe values, and kinds describe types. Therefore, turning on `DataKinds` “lifts” each definition by a single level, so types become kinds and values become types. This permits using these things at the type level: - -```haskell -newtype UserId (s :: RegistrationStatus) = UserId Text -``` - -In this example, `UserId` still has a single phantom type variable, `s`, but this time it is constrained to the `RegistrationStatus` kind. Therefore, it can *only* be `Registered` or `Anonymous`. This cooperates well with the aforementioned `StandaloneDeriving` mechanism, and it mostly provides a convenient way to constrain type variables to custom kinds. - -In general, `DataKinds` is a much more powerful extension, allowing things like type-level natural numbers or strings, which can be used to perform actual type-level computation (especially in combination with `TypeFamilies`) or a sort of metaprogramming. In some cases, they can even be used to implement things emulating things you can do with dependent types. - -I think `DataKinds` are a very cool Haskell extension, but there are a couple caveats. One of the main ones is how new kinds are defined: `DataKinds` “hijacks” the existing datatype declaration syntax by making every single datatype declaration define a type *and* a kind. This is a little confusing, and it would be nice if a different syntax was used so that each could be defined independently. - -Similarly, it seems that a lot of work is being done to allow using runtime values at the type level, but I wonder if people will ever need to use, say, runtime values at the *kind* level. This immediately evokes thoughts of Racket’s phase-based macro system, and I wonder if some of this duplication would be unnecessary with something similar. - -Food for thought, but overall, `DataKinds` are a very nice addition to help with precisely and specifically typing particular problems. - -## Typeclasses can emulate effects - -This is something that I’ve found interesting in my time writing Haskell because I have *no idea* if it’s idiomatic or not, but it seems pretty powerful. The initial motivator for this idea was figuring out how to test our code without constantly dropping into `IO`. - -More generally, we wanted to be able to unit test by “mocking” out collaborators, as it would be described in object oriented programming. I was always semi-distrustful of mocking, and indeed, it seems likely that it is heavily abused in certain circles, but I’ve come to appreciate the need that sometimes it is important to stub things out, *even in pure code*. - -As an example, consider some code that needs access to the current time. This is something that would normally require `IO`, but we likely want to be able to use the value in a pure context without “infecting” the entire program with `IO` types. In Haskell, I have generally seen three ways of handling this sort of thing: - - 1. Just inject the required values into the function and produce them “higher up” where I/O is okay. If threading the value around becomes too burdensome, use a Reader monad. - - 2. Use a free monad or similar to create a pure DSL of sorts, then write interpreters for various implementations, one of which uses `IO`. - - 3. Create custom monadic typeclasses that provide interfaces to the functionality you want to perform, then create instances, one of which is an instance over `IO`. - -This last approach seems to be less common in Haskell, but it’s the approach we took, and it seems to work out remarkably well. Returning to the need to get the current time, we could pretty easily write such a typeclass to encode that need: - -```haskell -class Monad m => CurrentTime m where - getCurrentTime :: m UTCTime -``` - -Now we can write functions that use the current time: - -```haskell -validateToken :: CurrentTime m => Token -> m Bool -validateToken tok = do - currentTime <- getCurrentTime - return (tokenExpirationDate tok > currentTime) -``` - -Now, we can write instances for `CurrentTime` that will allow us to run the same code in different contexts: - -```haskell -newtype AppM a = AppM { runAppM :: IO a } - deriving (Functor, Applicative, Monad, MonadIO) - -newtype TestM a = TestM (Identity a) - deriving (Functor, Applicative, Monad) - -runTestM :: TestM a -> a -runTestM (TestM x) = runIdentity x - -instance CurrentTime AppM where - getCurrentTime = liftIO Data.Time.Clock.getCurrentTime - -instance CurrentTime TestM where - getCurrentTime = return $ posixSecondsToUTCTime 0 -``` - -Where this really starts to shine is when adding additional effects. For example, the above token validation function might also need information about some kind of secret used for signing. Under this model, it’s just another typeclass: - -```haskell -class Monad m => TokenSecret m where - getTokenSecret :: m Secret - -validateToken :: (CurrentTime m, TokenSecret m) => Token -> m Bool -validateToken tok = do - currentTime <- getCurrentTime - secret <- getTokenSecret - return (tokenExpirationDate tok > currentTime - && verifySignature tok secret) -``` - -Of course, so far all of these functions have been extremely simple, and we’ve basically been using them as a glorified reader monad. In practice, though, we use this pattern for lots more than just retrieving values. For example, we might have a typeclass for database interactions: - -```haskell -class Monad m => Persistence m where - fetchUser :: Id User -> m (Maybe User) - insertUser :: User -> m (Either PersistenceError (Id User)) -``` - -With all of this done, it becomes incredibly easy to see which functions are using which effects: - -```haskell -postUsers - :: (CurrentTime m, Persistence m, TokenSecret m) - => User -> m Response -postUsers = ... - -getHealthcheck - :: CurrentTime m - => m Response -getHealthcheck = ... -``` - -There’s no need to perform any lifting, and this all seems to scale quite nicely. We’ve written some additional utilities to help write tests against functions using these kinds of monadic interfaces, and even though there’s a little bit of annoying boilerplate in a few spots, overall it seems to work quite elegantly. - -I’m not entirely sure how common this is in the Haskell community, but it’s certainly pretty neat how easy it is to get nearly all of the benefits of effect types in other languages simply by composing some of Haskell’s simplest features. - -## Atom’s ide-haskell tooling is invaluable - -Alright, so, confession time: I don’t use Emacs. - -I know, I know, how is that possible? I write Lisp, after all. Well, honestly, I tried picking it up a number of times, but none of those times did I get far enough to ditch my other tools. For Racket work, I use DrRacket, but for almost everything else, I use Atom. - -Atom has a lot of flaws, but it’s also pretty amazing in places, and I absolutely *love* the Haskell tooling written by the wonderful [atom-haskell][] folks. I use it constantly, and even though it doesn’t always work perfectly, it works pretty well. When it has problems, I’ve at least figured out how to get it working correctly. - -This is probably hard to really explain without seeing it for yourself, but I’ve found that I basically *depend* on this sort of tooling to be fully productive in Haskell, and I have no problem admitting that. The ability to get instant feedback about type errors tied to visual source locations, to be able to directly manipulate the source by selecting expressions and getting type information, and even the option to get inline linter suggestions means I spend a lot less time glancing at the terminal, and even less time in the REPL. - -The tooling is far from perfect, and it leaves a lot to be desired in places (the idea of using that static information for automated, project-wide refactoring *a la* Java is tantalizing), but most of those things are ideas of what amazing things could be, not broken or missing essentials. I am pretty satisfied with ide-haskell right now, and I can only hope it continues to get better and better. - -# Frustrations, drawbacks, and pain points - -Haskell is not perfect. In fact, far from it. There is a vast array of little annoyances that I have with the language, as is the case with any language. Still, there are a few overarching problems that I would really like to at least mention. These are the biggest sources of frustration for me so far. - -## Purity, failure, and exception-handling - -One of Haskell’s defining features is its purity—I don’t think many would disagree with that. Some people consider it a drawback, others consider it one of its greatest boons. Personally, I like it a lot, and I think one of the best parts about it is how it requires the programmer to be incredibly deliberate about failure. - -In many languages, when looking up a value from a container where the key doesn’t exist, there are really two ways to go about expressing this failure: - - 1. Throw an exception. - 2. Return `null`. - -The former is scary because it means *any* call to any function can make the entire program blow up, and it’s often impossible to know which functions even have the potential to throw. This creates a certain kind of non-local control flow that can sometimes cause a lot of unpredictability. The second option is much the same, especially when any value in a program might be `null`; it just defers the failure. - -In languages with option types, this is somewhat mitigated. Java now has option types, too, but they are still frequently cumbersome to use because there is nothing like monads to use to simply chain operations together. Haskell, in comparison, has an incredible complement of tools to simply handle errors without a whole lot of burden on the programmer, and I have found that, in practice, this is *actually helpful* and I really do write better error-handling code. - -### First, the good parts - -I have seen a comparison drawn between throwing checked exceptions and returning `Maybe` or `Either` types, but in practice the difference is massive. Handling checked exceptions is a monotonous chore because they are not first-class values, they are actually entirely separate linguistic constructs. Consider a library that throws a `LibraryException`, and you want to wrap that library and convert those exceptions to `ApplicationException`s. Well, have fun writing this code dozens of times: - -```java -try { - x = doSomething(); -} catch (LibraryException ex) { - throw ApplicationException.fromLibraryException(ex); -} - -// ... - -try { - y = doSomethingElse(); -} catch (LibraryException ex) { - throw ApplicationException.fromLibraryException(ex); -} -``` - -In Haskell, failure is just represented by first-class values, and it’s totally possible to write helper functions to abstract over that kind of boilerplate: - -```haskell -libraryToApplication :: LibraryError -> ApplicationError -libraryToApplication = ... - -liftLibrary :: Either LibraryError a -> Either ApplicationError a -liftLibrary = mapLeft libraryToApplication -``` - -Now, that same boilerplate-y code becomes nearly invisible: - -```haskell -x <- liftLibrary doSomething - --- ... - -y <- liftLibrary doSomethingElse -``` - -This might not *seem* like much, but it really cuts down on the amount of visual noise, which ends up making all the difference. Boilerplate incurs a cost much bigger than simply taking the time to type it all out (though that’s important, too): the cognitive overhead of parsing which parts of a program are boilerplate has a significant impact on readability. - -### So what’s the problem? - -If error handling is so great in Haskell, then why am I putting it under the complaints section? Well, it turns out that not everyone seems to think it’s as great as I make it out to be because people seem to keep writing Haskell APIs that throw exceptions! - -Despite what some purists would have you believe, Haskell has exceptions, and they are not uncommon. Lots of things can throw exceptions, some of which are probably reasonable. Failing to connect to a database is a pretty catastrophic error, so it seems fair that it would throw. On the other hand, inserting a duplicate record is pretty normal operation, so it seems like that should *not* throw. - -I mostly treat exceptions in Haskell as unrecoverable catastrophes. If I throw an error in *my* code, I do not intend to catch it. That means something horrible happened, and I just want that horribleness to show up in a log somewhere so I can fix the problem. If I care about failure, there are better ways to handle that failure gracefully. - -It’s also probably worth noting that exceptions in Haskell can be thrown from anywhere, even pure code, but can only be *caught* within the `IO` monad. This is especially scary, but I’ve seen it happen in actual libraries out in the wild, even ones that the entire Haskell ecosystem is built on. One of the crowning examples of this is the `text` package, which provides a function called `decodeUtf8` to convert bytestrings into text. Its type is very simple: - -```haskell -decodeUtf8 :: ByteString -> Text -``` - -But wait, what if the bytestring is not actually a valid UTF-8 string? - -Boom. There goes the application. - -Okay, okay, well, at least the `text` package provides another function, this one called `decodeUtf8'`, which returns an `Either`. This is good, and I’ve trained myself to only ever use `decodeUtf8'`, but it still has some pretty significant problems: - - - The *safe* version of this function is the “prime” version, rather than the other way around, which encourages people to use the unsafe one. Ideally, the unsafe one should be explicitly labeled as such... maybe call it `unsafeDecodeUtf8`? - - - This is not a hypothetical problem. When using a Haskell JWT library, we found a function that converts a string into a JWT. Since not all strings are JWTs, the library intelligently returns a `Maybe`. Therefore, we figured we were safe. - - A couple weeks later, we found that providing this function with invalid data was returning HTTP 500 errors. Why? Our error handling was meticulous! Well, the answer was a `decodeUtf8` call, hidden inside of the JWT library. This is especially egregious, given that the API it exposed returned a `Maybe` anyway! It would have been trivial to use the safe version there, instead, but the poor, misleading name led the library developer to overlook the bug lurking in the otherwise innocuous function. - - Even worse, this function was totally pure, and we used it in pure code, so we could not simply wrap the function and catch the exception. We had two options: use `unsafePerformIO` (yuck!) or perform a check before handing the data to the buggy function. We chose the latter, but in some cases, I imagine that could be too difficult to do in order to make it feasible. - -The point I’m trying to make is that this is a real problem, and it seems to me that throwing exceptions invalidates one of the primary advantages of Haskell. It disappointed me to realize that a significant amount of code written by FP Complete, one of the primary authors of some of the most important “modern Haskell” code in existence (including Stack), seem to very frequently expose APIs that will throw. - -I’m not sure how much of this stems from a fundamental divide in the Haskell ecosystem and how much it is simply due to Michael Snoyman’s coding style, given that he is the primary author of a number of these tools and libraries that seem very eager to throw exceptions. As just one example of a real situation in which we were surprised by this behavior, we used Snoyman’s http-client library and found that it mysteriously throws upon nearly *any* failure state: - -> A note on exceptions: for the most part, all actions that perform I/O should be assumed to throw an `HttpException` in the event of some problem, and all pure functions will be total. For example, `withResponse`, `httpLbs`, and `BodyReader` can all throw exceptions. - -This doesn’t seem entirely unreasonable—after all, isn’t a failure to negotiate TLS fairly catastrophic?—until you consider our use case. We needed to make a subrequest during the extent of another HTTP request to our server, and if that subrequest fails, we absolutely need to handle that failure gracefully. Of course, this is not *terrible* given that we are in `IO` so we can actually catch these exceptions, but since this behavior was only noted in a single aside at the top of the documentation, we didn’t realize we were forgetting error handling until far too late and requests were silently failing. - -Exceptions seem to devalue one of the most powerful concepts in Haskell: if I don’t consider all the possibilities, my code *does not compile*. In practice, when working with APIs that properly encode these possibilities into the type system, this value proposition seems to be real. I really do find myself writing code that works correctly as soon as it compiles. It’s almost magical. - -Using exceptions throws that all out the window, and I wish the Haskell ecosystem was generally more cautious about when to use them. - -## The String problem - -I sort of alluded to this a tiny bit in the last section, and that is probably indicative of how bad this issue is. I’m just going to be blunt: **In Haskell, strings suck.** - -This is always a bit of an amusing point whenever it is discussed because of how silly it seems. Haskell is a research language with a cutting-edge type system and some of the fanciest features of any language in existence. When everyday programming might use things like “profunctors”, “injective type families”, and “generalized algebraic datatypes”, you would think that dealing with *strings* would be a well-solved problem. - -But it isn’t. Haskell libraries frequently use not one, not two, but ***five*** kinds of strings. Let’s list them off, shall we? - - - First off, there’s the built-in `String` type, which is actually an alias for the `[Char]` type. For those not intimately familiar with Haskell, that’s a *linked list of characters*. As [Stephen Diehl][stephendiehl] recently put it in [a blog post describing the disaster that is Haskell string types](http://www.stephendiehl.com/posts/strings.html): - - > This is not only a bad representation, it’s quite possibly the least efficient (non-contrived) representation of text data possible and has horrible performance in both time and space. *And it’s used everywhere in Haskell.* - - The point is, it’s really bad. This type is not a useful representation for textual data in practical applications. - - - Moving on, we have a fairly decent type, `Text`, which comes from `Data.Text` in the `text` package. This is a decent representation of text, and it’s probably the one that everything should use. Well, maybe. Because `Text` comes in two varieties: lazy and strict. Nobody seems to agree on which of those two should be used, though, and they are totally incompatible types: functions that work with one kind of text won’t work with the other. You have to manually convert between them. - - - Finally, we have `ByteString`, which is horribly misnamed because it really isn’t a string at all, at least not in the textual sense. A better name for this type would have simply been `Bytes`, which sounds a lot scarier. And that would be good, because data typed as a `ByteString` is as close as you can get in Haskell to not assigning a type at all: a bytestring holds arbitrary bytes without assigning them any meaning whatsoever! - - Or at least, that’s the intention. The trouble is that people *don’t* treat bytestrings like that—they just use them to toss pieces of text around, even when those pieces of text have a well-defined encoding and represent textual data. This leads to the `decodeUtf8` problem mentioned above, but it’s bigger than that because it often ends up with some poor APIs that assign some interpretation to `ByteString` data without assigning it a different type. - - Again, this is throwing away so much of Haskell’s safety. It would be like using `Int` to keep track of boolean data (“just use 0 and 1!”) or using empty and singleton lists instead of using `Maybe`. When you use the precise type, you encode invariants and contracts into statically-checked assertions, but when you use general types like `ByteString`, you give that up. - - Oh, and did I mention that `ByteString`s also come in incompatible lazy and strict versions, too? - -So, obviously, the answer is to just stop using the bad types and to just use (one kind of) `Text` everywhere. Great! Except that the other types are totally inescapable. The entire standard library uses `String` exclusively—after all, `text` is a separate package—and small libraries often use `String` instead of `text` because they have no need to bring in the dependency. Of course, this just means every real application pays the performance hit of converting between all these different kinds of strings. - -Similarly, those that *do* use `Text` often use different kinds of text, so code ends up littered with `fromStrict` or `toStrict` coercions, which (again) have a cost. I’ve already ranted enough about `ByteString`, but basically, if you’re using `ByteString` in your API to pass around data that is semantically text, you are causing me pain. Please stop. - -It seems that the way `Data.Text` probably *should* have been designed was by making `Text` a typeclass, then making the lazy and strict implementations instances of that typeclass. Still, the fact that both of them exist would always cause problems. I’m actually unsure which one is the “correct” choice—I don’t know enough about how the two perform in practice—but it seems likely that picking *either* one would be a performance improvement over the current system, which is constantly spending time converting between the two. - -This issue has been ranted about plenty, so I won’t ramble on, but if you’re designing new libraries, please, *please* use `Text`. Your users will thank you. - -## Documentation is nearly worthless - -Finally, let’s talk about documentation. - -One of my favorite programming languages is Racket. Racket has a documentation tool called Scribble. Scribble is special because it is a totally separate domain-specific language for writing documentation, and it makes it fun and easy to write good explanations. There are even forms for typesetting automatically-rendered examples that look like a REPL. If the examples ever break or become incorrect, the docs don’t even compile. - -All of the Racket core library documentation makes sure to set a good example about what good documentation should look like. The vast majority of the documentation is paragraphs of prose and simple but practical examples. There are also type signatures (in the form of contracts), and those are super important, but they are so effective because of how the prose explains what each function does, when to use it, *why* you’d use it, and *why you wouldn’t use it*. - -Everything is cross-referenced automatically. The documentation is completely searchable locally out of the box. As soon as you install a package, its docs are automatically indexed. User-written libraries tend to have pretty good docs, too, because the standard libraries set such a good example *and* because the tools are so fantastic. Racket docs are really nice, and they’re so good they actually make things like Stack Overflow or even Google mostly irrelevant. It’s all there in the manual. - -Haskell documentation is the opposite of everything I just said. - - - The core libraries are poorly documented. Most functions include a sentence of description, and almost none include examples. At their worst, the descriptions simply restate the type signature. - - - Third-party libraries’ documentation is even worse, going frequently completely undocumented and actually only including type signatures and nothing else. - - - Haddock is an incredibly user-hostile tool for writing anything other than tiny snippets of documentation and is not very good at supporting prose. Notably, Haddock’s documentation is not generated using Haddock (and it still manages to be almost unusable). Forcing all documentation into inline comments makes users unlikely to write much explanation, and there is no ability for abstraction. - - - Reading documentation locally is very difficult because there is no easy way to open documentation for a particular package in a web browser, and it’s *certainly* not searchable. This is especially ridiculous given that Hoogle exists, which is one of best ways to search API docs in existence. There should be a `stack hoogle` command that just opens a Hoogle page for all locally-installed packages and Just Works, but there isn’t. - - - Most valuable information exists outside of documentation, so Google becomes a go-to immediately after a quick glance at the docs, and information is spread across blog posts, mailing lists, and obscure reddit posts. - -This is a problem that cannot be fixed by just making Haddock better, nor can it be fixed simply by improving the existing standard library documentation. There is a fundamental problem with Haskell documentation (which, to be completely fair, is not unique to Haskell), which is that its tools do not support anything more than API docs. - -Good documentation is so much more than “here’s what this function does”; it’s about guides and tutorials and case studies and common pitfalls. [This is documentation for someone new to lenses.][racket-lens] [This is not.][haskell-lens] Take note of the difference. - -# Conclusion and other thoughts - -Haskell is an incredible programming platform, and indeed, it is sometimes mind-boggling how complete it is. It also has a lot of rough edges, sometimes in places that feel like they need a lot more care, or perhaps they’re even simply unfinished. - -I could spend weeks writing about all the things I really like or dislike about the language, discussing in fine detail all the things that have made me excited or all the little bits that have made me want to tear my hair out. Heck, I could probably spend a month writing about strings alone. That’s not the point, though... I took a risk with Haskell, and it’s paid off. I’m not yet sure exactly how I feel about it, or when I would chose it relative to other tools, but it is currently very high on my list of favorite technologies. - -I did not come to Haskell with a distaste for static typing, despite the fact that I write so much Racket, a dynamically typed language (by default, at least). I don’t really use Typed Racket, and despite my love for Haskell and its type system, I am not sure I will use much more of it than I did before. Haskell and Racket are very different languages, which is justified in some places and probably sort of circumstantial in others. - -The future of Haskell seems bright, and a lot of the changes in the just-released GHC 8 are extremely exciting. I did not list records as a pain point because the changes in GHC 8 appear to make them a *lot* more palatable, although whether or not they solve that problem completely remains to be seen. I will absolutely continue to write Haskell and push it to its limits where I can, and hopefully try and take as much as I can from it along the way. - -[atom-haskell]: https://github.com/atom-haskell -[haskell-lens]: https://hackage.haskell.org/package/lens#readme -[racket-lens]: http://docs.racket-lang.org/lens/lens-guide.html -[spolsky-wrong]: http://www.joelonsoftware.com/articles/Wrong.html -[stephendiehl]: http://www.stephendiehl.com/ -[text-conversions]: https://hackage.haskell.org/package/text-conversions diff --git a/blog/posts/2016-08-11-climbing-the-infinite-ladder-of-abstraction.md b/blog/posts/2016-08-11-climbing-the-infinite-ladder-of-abstraction.md deleted file mode 100644 index c9ed79b..0000000 --- a/blog/posts/2016-08-11-climbing-the-infinite-ladder-of-abstraction.md +++ /dev/null @@ -1,145 +0,0 @@ - Title: Climbing the infinite ladder of abstraction - Date: 2016-08-11T18:03:48 - Tags: programming languages, racket, haskell - -I started programming in elementary school. - -When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to [solve the general problem][xkcd-general-problem]. When I learned about programming, I was immediately hooked: it was *so easy* to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again. - -Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of **abstraction**. - -# The brick wall of inexpressiveness - -When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this: - -```java -public String getName() { - return this.name; -} - -public void setName(String name) { - this.name = name; -} -``` - -This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other. - -It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The *whole reason* I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons. - -When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could. - -Both of these languages were flawed, as all languages are, but they opened my mind to the idea that *programming languages* could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all. - -# Discovering Lisp - -Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made. - -This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme? - -The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a [highly dynamic Lisp with a prototypal object system called Sol][sol-lang]. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased. - -Unfortunately, it was also abysmally slow. - -I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called [Racket][racket-lang]. At about the same time, someone pointed me to a totally different language called [Haskell][haskell-lang]. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then. - -# A journey into complexity - -Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned *so much more* since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language. - -I’ve also started realizing something else, though: **the languages I’ve settled into are *really complicated*.** - -When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages. - -Why? - -Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.” - -Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it? - -I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems. - -Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I *still* have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained? - -## Improvement, but never mastery - -Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others. - -For example I am pretty confident that I *get* JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques *within* the language, not entirely new linguistic constructs. - -On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about `TypeInType`? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do *not* have a good grasp on all the intricacies of Racket’s macro system. - -This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out `DataKinds`, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused. - -Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is *terrible*, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up. - -Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even *more* advanced things, and still, none of them truly solve my problems. - -# Ultimately, it all has (at least a little) value - -It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t. - -The scariest part of all is that I think it’s completely worthwhile. - -So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs *seem* unnecessarily complicated, they *seem* increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us. - -The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history. - -Most programmers, even those who have never seen BASIC before, can figure out what this snippet does: - -```BASIC -10 INPUT "What is your name: "; U$ -20 PRINT "Hello "; U$ -``` - -On the other hand, very few would probably understand this one: - -```haskell --- | A class for categories. --- id and (.) must form a monoid. -class Category cat where - -- | the identity morphism - id :: cat a a - - -- | morphism composition - (.) :: cat b c -> cat a b -> cat a c -``` - -Yet very few new programs are being written in BASIC, and lots are being written in Haskell. - -Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax: - -```js -export const composeWithProps = curry((a, parentProps, b) => { - const composed = childProps => - createElement(a, parentProps, createElement(b, omit(['children'], childProps), childProps.children)); - // give the composed component a pretty display name for debugging - composed.displayName = `Composed(${getDisplayName(a)}, ${getDisplayName(b)})`; - return composed; -}); -``` - -Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer. - -That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an *extremely* productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them. - -## Reactionary anti-intellectualism and the search for moderation - -I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. [I recently tweeted a picture of some rather dense mathematics from a paper I’d read][math-tweet], and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical. - -Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like `map` should be banned because they are too confusing and not immediately self-explanatory. - -I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message. - -Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern *technology* possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint. - -Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with. - -I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular. - -Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time. - -[haskell-lang]: https://www.haskell.org -[math-tweet]: https://twitter.com/lexi_lambda/status/763111451691134976 -[racket-lang]: http://racket-lang.org -[sol-lang]: https://github.com/lexi-lambda/libsol -[xkcd-general-problem]: https://xkcd.com/974/ diff --git a/blog/posts/2016-08-24-understanding-the-npm-dependency-model.md b/blog/posts/2016-08-24-understanding-the-npm-dependency-model.md deleted file mode 100644 index 2a5ee04..0000000 --- a/blog/posts/2016-08-24-understanding-the-npm-dependency-model.md +++ /dev/null @@ -1,148 +0,0 @@ - Title: Understanding the npm dependency model - Date: 2016-08-24T22:50:28 - Tags: javascript - -Currently, [npm][npm] is *the* package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like [Bower][bower] are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects **you** as a user or package developer. - -# First, the basics - -At a high level, npm is not too dissimilar from other package managers for programming languages: packages depend on other packages, and they express those dependencies with *version ranges*. npm happens to use the [semver][semver] versioning scheme to express those ranges, but the way it performs version resolution is mostly immaterial; what matters is that packages can depend on ranges rather than specific versions of packages. - -This is rather important in any ecosystem, since locking a library to a specific set of dependencies could cause significant problems, but it’s actually much less of a problem in npm’s case compared to other, similar package systems. Indeed, it is often safe for a library author to pin a dependency to a specific version without affecting dependent packages or applications. The tricky bit is determining *when* this is safe and when it’s not, and this is what I so frequently find that people get wrong. - -# Dependency duplication and the dependency tree - -Most users of npm (or at least most package authors) eventually learn that, unlike other package managers, npm installs a *tree* of dependencies. That is, every package installed gets its own set of dependencies rather than forcing every package to share the same canonical set of packages. Obviously, virtually every single package manager in existence has to model a dependency tree at some point, since that’s how dependencies are expressed by programmers. - -For example, consider two packages, `foo` and `bar`. Each of them have their own set of dependencies, which can be represented as a tree: - -``` -foo -├── hello ^0.1.2 -└── world ^1.0.7 - -bar -├── hello ^0.2.8 -└── goodbye ^3.4.0 -``` - -Imagine an application that depends on *both* `foo` and `bar`. Obviously, the `world` and `goodbye` dependencies are totally unrelated, so how npm handles them is relatively uninteresting. However, consider the case of `hello`: both packages require conflicting versions. - -Most package managers (including RubyGems/Bundler, pip, and Cabal) would simply barf here, reporting a version conflict. This is because, in most package management models, **only one version of any particular package can be installed at a time**. In that sense, one of the package manager’s primary responsibilities is to figure out a set of package versions that will satisfy every version constraint simultaneously. - -In contrast, npm has a somewhat easier job: it’s totally okay with installing different versions of the same package because each package gets its own set of dependencies. In the aforementioned example, the resulting directory structure would look something like this: - -``` -node_modules/ -├── foo/ -│ └── node_modules/ -│ ├── hello/ -│ └── world/ -└── bar/ - └── node_modules/ - ├── hello/ - └── goodbye/ -``` - -Notably, the directory structure very closely mirrors the actual dependency tree. The above diagram is something of a simplification: in practice, each transitive dependency would have its own `node_modules` directory and so on, but the directory structure can get pretty messy pretty quickly. (Furthermore, npm 3 performs some optimizations to attempt to share dependencies when it can, but those are ultimately unnecessary to actually understanding the model.) - -This model is, of course, extremely simple. The obvious effect is that every package gets its own little sandbox, which works absolutely marvelously for utility libraries like `ramda`, `lodash`, or `underscore`. If `foo` depends on `ramda@^0.19.0` but `bar` depends on `ramda@^0.22.0`, they can both coexist completely peacefully without any problems. - -At first blush, this system is *obviously* better than the alternative, flat model, so long as the underlying runtime supports the required module loading scheme. However, it is not without drawbacks. - -The most apparent downside is a significant increase in code size, given the potential for many, many copies of the same package, all with different versions. An increase in code size can often mean more than just a larger program—it can have a significant impact on performance. Larger programs just don’t fit into CPU caches as easily, and merely having to page a program in and out can significantly slow things down. That’s mostly just a tradeoff, though, since you’re sacrificing performance, not program correctness. - -The more insidious problem (and the one that I see crop up quite a lot in the npm ecosystem without much thought) is how dependency isolation can affect cross-package communication. - -# Dependency isolation and values that pass package boundaries - -The earlier example of using `ramda` is a place where npm’s default dependency management scheme really shines, given that Ramda just provides a bunch of plain ol’ functions. Passing these around is totally harmless. In fact, mixing functions from two different versions of Ramda would be totally okay! Unfortunately, not all cases are nearly that simple. - -Consider, for a moment, `react`. React components are very much *not* plain old data; they are complex values that can be extended, instantiated, and rendered in a variety of ways. React represents component structure and state using an internal, private format, using a mixture of carefully arranged keys and values and some of the more powerful features of JavaScript’s object system. This internal structure might very well change between React versions, so a React component defined with `react@0.3.0` likely won’t work quite right with `react@15.3.1`. - -With that in mind, consider two packages that define their own React components and export them for consumers to use. Looking at their dependency tree, we might see something like this: - -``` -awesome-button -└── react ^0.3.0 - -amazing-modal -└── react ^15.3.1 -``` - -Given that these two packages use wildly different versions of React, npm would give each of them their own copy of React, as requested, and packages would happily install. However, if you tried to use these components together, they wouldn’t work at all! A newer version of React simply cannot understand an old version’s component, so you would get a (likely confusing) runtime error. - -What went wrong? Well, dependency isolation works great when a package’s dependencies are purely implementation details, never observable from outside of a package. However, as soon as a package’s dependency becomes exposed as part of its *interface*, dependency isolation is not only subtly wrong, it can cause complete failure at runtime. These are cases when traditional dependency management are much better—they will tell you as soon as you attempt to install two packages that they just don’t work together, rather than waiting for you to figure that out for yourself. - -This might not sound *too* bad—after all, JavaScript is a very dynamic language, so static guarantees are mostly few and far between, and your tests should catch these problems should they arise—but it can cause unnecessary issues when two packages *can* theoretically work together fine, but because npm assigned each one its own copy of a particular package (that is, it wasn’t quite smart enough to figure out it could give them both the same copy), things break down. - -Looking outside of npm specifically and considering this model when applied to other languages, it becomes increasingly clear that this won’t do. This blog post was inspired by [a Reddit thread discussing the npm model applied to Haskell][reddit-haskell-npm], and this flaw was touted as a reason why it couldn’t possibly work for such a static language. - -Due to the way the JavaScript ecosystem has evolved, it’s true that most people can often get away with this subtle potential for incorrect behavior without any problems. Specifically, JavaScript tends to rely on duck typing rather than more restrictive checks like `instanceof`, so objects that satisfy the same protocol will still be compatible, even if their implementations aren’t *quite* the same. However, npm actually provides a robust solution to this problem that allows package authors to explicitly express these “cross-interface” dependencies. - -## Peer dependencies - -Normally, npm package dependencies are listed under a `"dependencies"` key in the package’s `package.json` file. There is, however, another, less-used key called `"peerDependencies"`, which has the same format as the ordinary dependencies list. The difference shows up in how npm performs dependency resolution: rather than getting its own copy of a peer dependency, a package expects that dependency to be provided by its dependent. - -This effectively means that peer dependencies are effectively resolved using the “traditional” dependency resolution mechanism that tools like Bundler and Cabal use: there must be one canonical version that satisfies everyone’s constraint. Since npm 3, things are a little bit less straightforward (specifically, peer dependencies are not automatically installed unless a dependent package explicitly depends on the peer package itself), but the basic idea is the same. This means that package authors must make a choice for each dependency they install: should it be a normal dependency or a peer dependency? - -This is where I think people tend to get a little lost, even those familiar with the peer dependency mechanism. Fortunately, the answer is relatively simple: is the dependency in question visible in *any place* in the package’s interface? - -This is sometimes hard to see in JavaScript because the “types” are invisible; that is, they are dynamic and rarely explicitly written out. However, just because the types are dynamic does not mean they are not there at runtime (and in the heads of various programmers), so the rule still holds: if the type of a function in a package’s public interface somehow depends on a dependency, it should be a peer dependency. - -To make this a little more concrete, let’s look at a couple of examples. First off, let’s take a look at some simple cases, starting with some uses of `ramda`: - -```js -import { merge, add } from 'ramda' - -export const withDefaultConfig = (config) => - merge({ path: '.' }, config) - -export const add5 = add(5) -``` - -The first example here is pretty obvious: in `withDefaultConfig`, `merge` is used purely as an implementation detail, so it’s safe, and it’s not part of the module’s interface. In `add5`, the example is a little trickier: the result of `add(5)` is a partially-applied function created by Ramda, so technically, a Ramda-created value is a part of this module’s interface. However, the contract `add5` has with the outside world is simply that it is a JavaScript function that adds five to its argument, and it doesn’t depend on any Ramda-specific functionality, so `ramda` can safely be a non-peer dependency. - -Now let’s look at another example using the `jpeg` image library: - -```js -import { Jpeg } from 'jpeg' - -export const createSquareBuffer = (size, cb) => - createSquareJpeg(size).encode(cb) - -export const createSquareJpeg = (size) => - new Jpeg(Buffer.alloc(size * size, 0), size, size) -``` - -In this case, the `createSquareBuffer` function invokes a callback with an ordinary Node.js `Buffer` object, so the `jpeg` library is an implementation detail. If that were the only function exposed by this module, `jpeg` could safely be a non-peer dependency. However, the `createSquareJpeg` function violates that rule: it returns a `Jpeg` object, which is an opaque value with a structure defined exclusively by the `jpeg` library. Therefore, a package with the above module *must* list `jpeg` as a peer dependency. - -This sort of restriction works in reverse, too. For example, consider the following module: - -```js -import { writeFile } from 'fs' - -export const writeJpeg = (filename, jpeg, cb) => - jpeg.encode((image) => fs.writeFile(filename, image, cb)) -``` - -The above module does not even *import* the `jpeg` package, yet it implicitly depends on the `encode` method of the `Jpeg` interface. Therefore, despite not even explicitly using it anywhere in the code, a package containing the above module should include `jpeg` as a peer dependency. - -They key is to carefully consider what contract your modules have with their dependents. If those contracts involve other packages in any way, they should be peer dependencies. If they don’t, they should be ordinary dependencies. - -# Applying the npm model to other programming languages - -The npm model of package management is more complicated than that of other languages, but it provides a real advantage: implementation details are kept as implementation details. In other systems, it’s quite possible to find yourself in “dependency hell”, when you personally know that the version conflict reported by your package manager is not a real problem, but because the package system must pick a single canonical version, there’s no way to make progress without adjusting code in your dependencies. This is extremely frustrating. - -This sort of dependency isolation is not the most advanced form of package management in existence—indeed, far from it—but it’s definitely more powerful than most other mainstream systems out there. Of course, most other languages could not adopt the npm model simply by changing the package manager: having a global package namespace can prevent multiple versions of the same package being installed at a *runtime* level. The reason npm is able to do what it does is because Node itself supports it. - -That said, the dichotomy between peer and non-peer dependencies is a little confusing, especially to people who aren’t package authors. Figuring out which packages need to go in which group is not always obvious or trivial. Fortunately, other languages might be able to help. - -Returning to Haskell, its strong static type system would potentially allow this distinction to be detected entirely automatically, and Cabal could actually report an error when a package used in an exposed interface was not listed as a peer dependency (much like how it currently prevents importing a transitive dependency without explicitly depending on it). This would allow helper function packages to keep on being implementation details while still maintaining strong interface safety. This would likely take a lot of work to get just right—managing the global nature of typeclass instances would likely make this much more complicated than a naïve approach would accommodate—but it would add a nice layer of flexibility that does not currently exist. - -From the perspective of JavaScript, npm has demonstrated that it can be a capable package manager, despite the monumental burden placed upon it by the ever-growing, ever-changing JS ecosystem. As a package author myself, I would implore other users to carefully consider the peer dependencies feature and work hard to encode their interfaces’ contracts using it—it’s a commonly misunderstood gem of the npm model, and I hope this blog post helped to shed at least a little more light upon it. - -[bower]: https://bower.io -[npm]: https://www.npmjs.com -[reddit-haskell-npm]: https://www.reddit.com/r/haskell/comments/4zc6y3/why_doesnt_cabal_use_a_model_like_that_of_npm/?ref=share&ref_source=link -[semver]: http://semver.org diff --git a/blog/posts/2016-10-01-using-types-to-unit-test-in-haskell.md b/blog/posts/2016-10-01-using-types-to-unit-test-in-haskell.md deleted file mode 100644 index ce85599..0000000 --- a/blog/posts/2016-10-01-using-types-to-unit-test-in-haskell.md +++ /dev/null @@ -1,423 +0,0 @@ - Title: Using types to unit-test in Haskell - Date: 2016-10-03T01:20:43 - Tags: haskell, testing - -Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators. - -Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation. - -# First, an aside on testing philosophy - -Testing methodology is a controversial topic within the larger programming community, and there are a multitude of different approaches. This blog post is about *unit testing*, an already nebulous term with a number of different definitions. For the purposes of this post, I will define a unit test as a test that stubs out collaborators of the code under test in some way. Accomplishing that in Haskell is what this is primarily about. - -I want to be clear that I do not think that unit tests are the only way to write tests, nor the best way, nor even always an applicable way. Depending on your domain, rigorous unit testing might not even make sense, and other forms of tests (end-to-end, integration, benchmarks, etc.) might fulfill your needs. - -In practice, though, implementing those other kinds of tests seems to be well-documented in Haskell compared to pure, object-oriented style unit testing. As my Haskell applications have grown, I have found myself wanting a more fine-grained testing tool that allows me to both test a piece of my codebase in isolation and also use my domain-specific types. This blog post is about that. - -With that disclaimer out of the way, let’s talk about testing in Haskell. - -# Drawing seams using types - -One of the primary attributes of unit tests in object-oriented languages, especially statically-typed ones, is the concept of “seams” within a codebase. These are internal boundaries between components of a system. Some boundaries are obvious—interactions with a database, manipulation of the file system, and performing I/O over the network, to name a few examples—but others are more subtle. Especially in larger codebases, it can be helpful to isolate two related but distinct pieces of functionality as much as possible, which makes them easier to reason about, even if they’re actually part of the same codebase. - -In OO languages, these seams are often marked using interfaces, whether explicitly (in the case of static languages) or implicitly (in the case of dynamic ones). By programming to an interface, it’s possible to create “fake” implementations of that interface for use in unit tests, effectively making it possible to stub out code that isn’t directly relevant to the code being tested. - -In Haskell, representing these seams is a lot less obvious. Consider a fairly trivial function that reverses a file’s contents on the file system: - -```haskell -reverseFile :: FilePath -> IO () -reverseFile path = do - contents <- readFile path - writeFile path (reverse contents) -``` - -This function is impossible to test without testing against a real file system. It simply performs I/O directly, and there’s no way to “mock out” the file system for testing purposes. Now, admittedly, this function is so trivial that a unit test might not seem worth the cost, but consider a slightly more complicated function that interacts with a database: - -```haskell -renderUserProfile :: Id User -> IO HTML -renderUserProfile userId = do - user <- fetchUser userId - posts <- fetchRecentPosts userId - - return $ div - [ h1 (userName user <> "’s Profile") - , h2 "Recent Posts" - , ul (map (li . postTitle) posts) - ] -``` - -It might now be a bit more clear that it could be useful to test the above function without running a real database and doing all the necessary context setup before each test case. Indeed, it would be nice if a test could just provide stubbed implementations for `fetchUser` and `fetchRecentPosts`, then make assertions about the output. - -One way to solve this problem is to pass the results of those two functions to `renderUserProfile` as arguments, turning it into a pure function that could be easily tested. This becomes obnoxious for functions of even just slightly more complexity, though (it is not unreasonable to imagine needing a handful of different queries to render a user’s profile page), and it requires significantly restructuring code simply because the tests need it. - -The above code is not only difficult to test, however—it has another problem, too. Specifically, both functions return `IO` values, which means they can effectively do *anything*. Haskell has a very strong type system for typing terms, but it doesn’t provide any guarantees about effects beyond a simple yes/no answer about function purity. Even though the `renderUserProfile` function should really only need to interact with the database, it could theoretically delete files, send emails, make HTTP requests, or do any number of other things. - -Fortunately, it’s possible to solve *both* problems—a lack of testability and a lack of type safety—using the same general technique. This approach is reminiscent of the interface-based seams of object-oriented languages, but unlike most object-oriented approaches, it provides additional type safety guarantees without the need to explicitly modify the code to support some kind of dependency injection. - -## Making implicit interfaces explicit - -Statically typed, object-oriented languages provide interfaces as a language construct to encode certain kinds of contracts into the type system, and Haskell has something similar. Typeclasses are, in many ways, an analog to OO interfaces, and they can be used in a similar way. In the above case, let’s write down interfaces that the `reverseFile` and `renderUserProfile` functions can use: - -```haskell -class Monad m => MonadFS m where - readFile :: FilePath -> m String - writeFile :: FilePath -> String -> m () - -class Monad m => MonadDB m where - fetchUser :: Id User -> m User - fetchRecentPosts :: Id User -> m [Post] -``` - -The really nice thing about these interfaces is that our function implementations don’t have to change *at all* to take advantage of them. In fact, all we have to change is their types: - -```haskell -reverseFile :: MonadFS m => FilePath -> m () -reverseFile path = do - contents <- readFile path - writeFile path (reverse contents) - -renderUserProfile :: MonadDB m => Id User -> m HTML -renderUserProfile userId = do - user <- fetchUser userId - posts <- fetchRecentPosts userId - - return $ div - [ h1 (userName user <> "’s Profile") - , h2 "Recent Posts" - , ul (map (li . postTitle) posts) - ] -``` - -This is pretty neat, since we haven’t had to alter our code at all, but we’ve managed to completely decouple ourselves from `IO`. This has the direct effect of both making our code more abstract (we no longer rely on the “real” file system or a “real” database, which makes our code easier to test) and restricting what our functions can do (just from looking at the type signatures, we know what side-effects they can perform). - -Of course, since we’re now coding against an interface, our code doesn’t actually do much of anything. If we want to actually use the functions we’ve written, we’ll have to define instances of `MonadFS` and `MonadDB`. When actually running our code, we’ll probably still use `IO` (or some monad transformer stack with `IO` at the bottom), so we can define trivial instances for that existing use case: - -```haskell -instance MonadFS IO where - readFile = Prelude.readFile - writeFile = Prelude.writeFile - -instance MonadDB IO where - fetchUser = SQL.fetchUser - fetchRecentPosts = SQL.fetchRecentPosts -``` - -Even if we go no further, **this is already incredibly useful**. By restricting the sorts of effects our functions can perform at the type level, it becomes a lot easier to see which code is interacting with what. This can be invaluable when working in a part of a moderately large codebase that you are unfamiliar with. Even if the only instance of these typeclasses is `IO`, the benefits are immediately apparent. - -Of course, this blog post is about testing, so we’re going to go further and take advantage of these seams we’ve now drawn. The question is: how? - -# Testing with typeclasses: an initial attempt - -Given that we now have functions depending on an interface instead of `IO`, we can create separate instances of our typeclasses for use in tests. Let’s start with the `renderUserProfile` function. We’ll create a simple wrapper around the `Identity` type, since we don’t actually care much about the “effects” of our `MonadDB` methods: - -```haskell -import Data.Functor.Identity - -newtype TestM a = TestM (Identity a) - deriving (Functor, Applicative, Monad) - -unTestM :: TestM a -> a -unTestM (TestM (Identity x)) = x -``` - -Now, we’ll create a trivial instance of `MonadDB` for `TestM`: - -```haskell -instance MonadDB TestM where - fetchUser _ = return User { userName = "Alyssa" } - fetchRecentPosts _ = return - [ Post { postTitle = "Metacircular Evaluator" } ] -``` - -With this instance, it’s now possible to write a simple unit test of the `renderUserProfile` function that doesn’t need a real database running at all: - -```haskell -spec = describe "renderUserProfile" $ do - it "shows the user’s name" $ do - let result = unTestM (renderUserProfile (intToId 1234)) - result `shouldContainElement` h1 "Alyssa’s Profile" - - it "shows a list of the user’s posts" $ do - let result = unTestM (renderUserProfile (intToId 1234)) - result `shouldContainElement` ul [ li "Metacircular Evaluator" ] -``` - -This is pretty nice, and running the above tests reveals a nice property of these kinds of isolated test cases: the test suite runs *really, really fast*. Communicating with a database, even in extremely simple ways, takes a measurable amount of time, especially with dozens of tests. In contrast, even with hundreds of tests, our unit test suite runs in less than a tenth of a second. - -This all seems to be successful, so let’s try and apply the same testing technique to `reverseFile`. - -## Testing side-effectful code - -Looking at the type signature for `reverseFile`, we have a small problem: - -```haskell -reverseFile :: MonadFS m => FilePath -> m () -``` - -Specifically, the return type is `()`. Making any assertions against the result of this function would be completely worthless, given that it’s guaranteed to be the same exact thing each time. Instead, `reverseFile` is inherently side-effectful, so we want to be able to test that it properly interacts with the file system in the correct way. - -In order to do this, a simple wrapper around `Identity` won’t be enough, but we can replace it with something more powerful: `Writer`. Specifically, we can use a writer monad to “log” what gets called in order to test side-effects. We’ll start by creating a new `TestM` type, just like last time: - -```haskell -newtype TestM a = TestM (Writer [String] a) - deriving (Functor, Applicative, Monad, MonadWriter [String]) - -logTestM :: TestM a -> [String] -logTestM (TestM w) = execWriter w -``` - -Using this slightly more powerful type, we can write a useful instance of `MonadFS` that will track the argument given to `writeFile`: - -```haskell -instance MonadFS TestM where - readFile _ = return "hello" - writeFile _ contents = tell [contents] -``` - -Again, the instance is quite simple, but it now enables us to write a straightforward unit test for `reverseFile`: - -```haskell -spec = describe "reverseFile" $ - it "reverses a file’s contents on the filesystem" $ do - let calls = logTestM (reverseFile "foo.txt") - calls `shouldBe` ["olleh"] -``` - -Again, quite simple to both implement and use, and the test itself is blindingly fast. There’s another problem, though, which is that we have technically left part of `reverseFile` untested: we’ve completely ignored the `path` argument. - -In this contrived example, it may seem silly to test something so trivial, but in real code, it’s quite possible that one would care very much about testing multiple different aspects about a single function. When testing `renderUserProfile`, this was not hard, since we could reuse the same `TestM` type and `MonadDB` instance for both test cases, but in the `reverseFile` example, we’ve ignored the path entirely. - -We *could* adjust our `MonadFS` instance to also track the path provided to each method, but this has a few problems. First, it means every test case would depend on all the various properties we are testing, which would mean updating every test case when we add a new one. It would also be simply impossible if we needed to track multiple types—in this particular case, it turns out that `String` and `FilePath` are actually the same type, but in practice, there may be a handful of disparate, incompatible types. - -Both of the above issues could be fixed by creating a sum type and manually filtering out the relevant elements in each test case, but a much more intuitive approach would be to simply have a separate instance for each case. Unfortunately, in Haskell, creating a new instance means creating an entirely new type. To illustrate how much duplication that would entail, we could create the following type and instance for testing proper propagation of the `path` argument: - -```haskell -newtype TestM' a = TestM' (Writer [FilePath] a) - deriving (Functor, Applicative, Monad, MonadWriter [FilePath]) - -logTestM' :: TestM' a -> [FilePath] -logTestM' (TestM' w) = execWriter w - -instance MonadFS TestM' where - readFile path = tell [path] >> return "" - writeFile path _ = tell [path] -``` - -Now it’s possible to add an extra test case that asserts that the proper path is provided to the two filesystem functions: - -```haskell -spec = describe "reverseFile" $ do - it "reverses a file’s contents on the filesystem" $ do - let calls = logTestM (reverseFile "foo.txt") - calls `shouldBe` ["olleh"] - - it "operates on the file at the provided path" $ do - let paths = logTestM' (reverseFile "foo.txt") - paths `shouldBe` ["foo.txt", "foo.txt"] -``` - -This works, but it’s ultimately unacceptably complicated. Our test harness code is now significantly larger than the actual tests themselves, and the amount of boilerplate is frustrating. Verbose test suites are especially bad, since forcing programmers to jump through hoops just to implement a single test reduces the likelihood that people will actually write good tests, if they write tests at all. In contrast, if writing tests is easy, then people will naturally write more of them. - -The above strategy to writing tests is not good enough, but it does reveal a particular problem: in Haskell, typeclass instances are not first-class values that can be manipulated and abstracted over, they are static constructs that can only be managed by the compiler, and users do not have a direct way to modify them. With some cleverness, however, we can actually create an approximation of first-class typeclass dictionaries, which will allow us to dramatically simplify the above testing mechanism. - -# Creating first-class typeclass instances - -In order to provide an easy way to construct instances, we need a way to represent instances as ordinary Haskell values. This is not terribly difficult, given that instances are conceptually just records containing a collection of functions. For example, we could create a datatype that represents an instance of the `MonadFS` typeclass: - -```haskell -data MonadFSInst m = MonadFSInst - { _readFile :: FilePath -> m String - , _writeFile :: FilePath -> String -> m () - } -``` - -To avoid namespace clashes with the actual method identifiers, the record fields are prefixed with an underscore, but otherwise, the translation is remarkably straightforward. Using this record type, we can easily create values that represent the two instances we defined above: - -```haskell -contentInst :: MonadWriter [String] m => MonadFSInst m -contentInst = MonadFSInst - { _readFile = \_ -> return "hello" - , _writeFile = \_ contents -> tell [contents] - } - -pathInst :: MonadWriter [FilePath] m => MonadFSInst m -pathInst = MonadFSInst - { _readFile = \path -> tell [path] >> return "" - , _writeFile = \path _ -> tell [path] - } -``` - -These two values represent two different implementations of `MonadFS`, but since they’re ordinary Haskell values, they can be manipulated and even *extended* like any other records. This can be extremely useful, since it makes it possible to create a sort of “base” instance, then have individual test cases override individual pieces of functionality piecemeal. - -Of course, although we’ve written these two instances, we have no way to actually use them. After all, Haskell does not provide a way to explicitly provide typeclass dictionaries. Fortunately, we can create a sort of “proxy” type that will use a reader to thread the dictionary around explicitly, and the instance can defer to the dictionary’s implementation. - -## Creating an instance proxy - -To represent our proxy type, we’ll use a combination of a `Writer` and a `ReaderT`; the former to implement the logging used by instances, and the latter to actually thread around the dictionary. Our type will look like this: - -```haskell -newtype TestM log a = - TestM (ReaderT (MonadFSInst (TestM log)) (Writer log) a) - deriving ( Functor, Applicative, Monad - , MonadReader (MonadFSInst (TestM log)) - , MonadWriter log - ) - -logTestM :: MonadFSInst (TestM log) -> TestM log a -> log -logTestM inst (TestM m) = execWriter (runReaderT m inst) -``` - -This might look rather complicated, and it is, but let’s break down exactly what it’s doing. - - 1. The `TestM` type includes two type parameters. The first is the type of value that will be logged (hence the name `log`), which corresponds to the argument to `Writer` from previous incarnations of `TestM`. Unlike those types, though, we want this version to work with any `Monoid`, so we’ll make it a type parameter. The second parameter is simply the type of the current monadic value, as before. - - 2. The type itself is defined as a wrapper around a small monad transformer stack, the first of which is `ReaderT`. The state threaded around by the reader is, in this case, the instance dictionary, which is `MonadFSInst`. - - However, recall that `MonadFSInst` accepts a type variable—the type of a monad itself—so we must provide `TestM log` as an argument to `MonadFSInst`. This slight bit of indirection allows us to tie the knot between the mutually dependent instances and proxy type. - - 3. The base monad in the transformer stack is `Writer`, which is used to actually implement the logging functionality, just like in prior cases. The only difference now is that the `log` type parameter now determines what the writer actually produces. - - 4. Finally, as before, we use `GeneralizedNewtypeDeriving` to derive all the relevant `mtl` classes, adding the somewhat wordy `MonadReader` constraint to the list. - -Using this single type, we can now implement a `MonadFS` instance that defers to the dictionary carried around within `TestM`’s reader state: - -```haskell -instance Monoid log => MonadFS (TestM log) where - readFile path = do - f <- asks _readFile - f path - writeFile path contents = do - f <- asks _writeFile - f path contents -``` - -This may seem somewhat boilerplate-y, and it is to some extent, but the important consideration is that this boilerplate only needs to be written *once*. With this in place, it’s now possible to write an arbitrary number of first-class instances that use the above mechanism without extending the mechanism at all. - -To see what actually using this code would look like, let’s update the `reverseFile` tests to use the new `TestM` implementation, as well as the `contentInst` and `pathInst` dictionaries from earlier: - -```haskell -spec = describe "reverseFile" $ do - it "reverses a file’s contents on the filesystem" $ do - let calls = logTestM contentInst (reverseFile "foo.txt") - calls `shouldBe` ["olleh"] - - it "operates on the file at the provided path" $ do - let paths = logTestM pathInst (reverseFile "foo.txt") - paths `shouldBe` ["foo.txt", "foo.txt"] -``` - -We can do a little bit better, though. Really, the definitions of `contentInst` and `pathInst` are specific to each test case. With ordinary typeclass instances, we cannot scope them to any particular block, but since `MonadFSInst` is just an ordinary Haskell datatype, we can manipulate them just like any other Haskell values. Therefore, we can just inline those instances’ definitions into the test cases themselves to keep them closer to the actual tests. - -```haskell -spec = describe "reverseFile" $ do - it "reverses a file’s contents on the filesystem" $ do - let contentInst = MonadFSInst - { _readFile = \_ -> return "hello" - , _writeFile = \_ contents -> tell [contents] - } - let calls = logTestM contentInst (reverseFile "foo.txt") - calls `shouldBe` ["olleh"] - - it "operates on the file at the provided path" $ do - let pathInst = MonadFSInst - { _readFile = \path -> tell [path] >> return "" - , _writeFile = \path _ -> tell [path] - } - let paths = logTestM pathInst (reverseFile "foo.txt") - paths `shouldBe` ["foo.txt", "foo.txt"] -``` - -This is pretty good. We’re now able to create inline instances of our `MonadFS` typeclass, which allows us to write extremely concise unit tests using ordinary Haskell typeclasses as system seams. We’ve managed to cut down on the boilerplate considerably, though we still have a couple problems. For one, this example only uses a single typeclass containing only two methods. A real `MonadFS` typeclass would likely have at least a dozen methods for performing various filesystem operations, and writing out the instance dictionaries for every single method, even the ones that aren’t used within the code under test, would be pretty frustratingly verbose. - -This problem is solvable, though. Since instances are just ordinary Haskell records, we can create a “base” instance that just throws an exception whenever the method is called: - -```haskell -baseInst :: MonadFSInst m -baseInst = MonadFSInst - { _readFile = error "unimplemented instance method ‘_readFile’" - , _writeFile = error "unimplemented instance method ‘_writeFile’" - } -``` - -Then code that only uses `readFile` could only override that particular method, for example: - -```haskell -let myInst = baseInst { _readFile = ... } -``` - -Normally, of course, this would be a terrible idea. However, since this is all just test code, it can be extremely useful in quickly figuring out what methods need to be stubbed out for a particular test case. Since all the code actually gets run at test time, attempts to use unimplemented instance methods will immediately raise an error, informing the programmer which methods need to be implemented to make the test pass. This can also help to significantly cut down on the amount of effort it takes to implement each test. - -Another problem is that our approach is specialized exclusively to `MonadFS`. What about functions that use both `MonadFS` *and* `MonadDB`, for example? Fortunately, that is not hard to solve, either. We can adapt the `MonadFSInst` type to include fields for all of the typeclasses relevant to our system, turning it into a generic test fixture of sorts: - -```haskell -data FixtureInst m = FixtureInst - { -- MonadFS - _readFile :: FilePath -> m String - , _writeFile :: FilePath -> String -> m () - - -- MonadDB - , _fetchUser :: Id User -> m User - , _fetchRecentPosts :: Id User -> m [Post] - } -``` - -Updating `TestM` to use `FixtureInst` instead of `MonadFSInst` is trivial, and all the rest of the infrastructure still works. However, this means that every time a new typeclass is added, three things need to be updated: - - 1. Its methods need to be added to the `FixtureInst` record. - 2. Those methods need to be given error-raising defaults in the `baseInst` value. - 3. An actual instance of the typeclass needs to be written for `TestM` that defers to the `FixtureInst` value. - -Furthermore, most of this manual manipulation of methods is required every time a particular typeclass changes, whether that means adding a method, removing a method, renaming a method, or changing a method’s type. This is especially frustrating given that all this code is really just mechanical boilerplate that could all be derived by the set of typeclasses being tested. - -That last point is especially important: aside from the instances themselves, every piece of boilerplate above is obviously possible to generate from existing types alone. With that piece of information in mind, we can do even better: we can use Template Haskell. - -# Removing the boilerplate using `test-fixture` - -The above code was not only rather boilerplate-heavy, it was pretty complicated. Fortunately, you don’t actually have to write it. Enter the library [`test-fixture`][test-fixture]: - -```haskell -import Control.Monad.TestFixture -import Control.Monad.TestFixture.TH - -mkFixture "FixtureInst" [''MonadFS, ''MonadDB] - -spec = describe "reverseFile" $ do - it "reverses a file’s contents on the filesystem" $ do - let contentInst = def - { _readFile = \_ -> return "hello" - , _writeFile = \_ contents -> log contents - } - let calls = logTestFixture (reverseFile "foo.txt") contentInst - calls `shouldBe` ["olleh"] - - it "operates on the file at the provided path" $ do - let pathInst = def - { _readFile = \path -> log path >> return "" - , _writeFile = \path _ -> log path - } - let paths = logTestFixture (reverseFile "foo.txt") pathInst - paths `shouldBe` ["foo.txt", "foo.txt"] -``` - -**That’s it.** The above code automatically generates everything you need to write fast, simple, deterministic unit tests in Haskell. The `mkFixture` function is a Template Haskell macro that expands into a definition quite similar to the `FixtureInst` type we wrote by hand, but since it’s automatically generated from the typeclass definitions, it never needs to be updated. - -The `logTestFixture` function replaces the `logTestM` function we wrote by hand, but it works exactly the same. The `Control.Monad.TestFixture` library also exports a `log` function that is a synonym for `tell . singleton`, but using `tell` directly still works if you prefer. - -The `mkFixture` function also generates a `Default` instance, which replaces the `baseInst` value defined earlier. It functions the same way, though, producing useful error messages that refer to the names of unimplemented typeclass methods that have not been stubbed out. - -This blog post is not a `test-fixture` tutorial—indeed, it is much more complicated than a `test-fixture` tutorial would be, since it covers what the library is really doing under the hood—but if you’re interested, I would highly recommend you take a look at the [`test-fixture` documentation on Hackage][test-fixture]. - -# Conclusion, credits, and similar techniques - -This blog post came about as the result of a need my coworkers and I found when writing Haskell code; we wanted a way to write unit tests quickly and easily, but we didn’t find much advice from the rest of the Haskell ecosystem. The `test-fixture` library is the result of that exploratory work, and we currently use it to test a significant portion of our Haskell code. - -It would be extremely unfair to suggest that I was the inventor of this technique or the inventor of the library. Two of my coworkers, [Joe Vargas][jxv] and [Greg Wiley][gwiley], came up with the general approach and wrote `Control.Monad.TestFixture`, and I simply wrote the Template Haskell macro to eliminate the boilerplate. With that in mind, I think I can say with some fairness that I think this technique is a joy to use when unit testing is a desirable goal, and I would definitely recommend it if you are interested in doing isolated testing in Haskell. - -The general technique of using typeclasses to emulate effects was in part inspired by the well-known `mtl` library. An alternate approach to writing unit-testable Haskell code is using free monads, but overall, I prefer this approach over free monads because the typeclass constraints add type safety in ways that free monads do not (at least not without additional boilerplate), and this approach also lends itself well to static analysis-based boilerplate reduction techniques. It has its own tradeoffs, though, so if you’ve had success with free monads, then I certainly make no claim this is a superior approach, just one that I’ve personally found pleasant. - -As a final note, if you *do* check out `test-fixture`, feel free to leave feedback by opening issues on [the GitHub issue tracker][test-fixture-issues]—even things like confusing documentation are worth a bug report. - -[gwiley]: https://github.com/aztecrex -[jxv]: https://github.com/jxv -[test-fixture]: http://hackage.haskell.org/package/test-fixture -[test-fixture-issues]: https://github.com/cjdev/test-fixture/issues diff --git a/blog/posts/2017-01-02-rascal-a-haskell-with-more-parentheses.md b/blog/posts/2017-01-02-rascal-a-haskell-with-more-parentheses.md deleted file mode 100644 index 49efdb9..0000000 --- a/blog/posts/2017-01-02-rascal-a-haskell-with-more-parentheses.md +++ /dev/null @@ -1,153 +0,0 @@ - Title: Rascal: a Haskell with more parentheses - Date: 2017-01-02T09:16:42 - Tags: hackett, racket, haskell, programming languages, functional programming - -> **Note**: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in [the followup blog post][rascal-is-hackett]. - -“Hey! You got your Haskell in my Racket!” - -“No, you got *your* Racket in *my* Haskell!” - -Welcome to the [Rascal][rascal] programming language. - -# Why Rascal? - -Why yet *another* programming language? Anyone who knows me knows that I already have two programming languages that I *really* like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used. - -Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that *is* something I want to write at some point). What you *should* understand, though, is that to me, Haskell is pretty close to what I want in a programming language. - -At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and *even then* the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use. - -Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one *fact* that I want to bring up, which is that **Haskell does not provide any mechanism for adding syntactic abstractions to the language**. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing. - -## A programmable programming language: theory and practice - -I feel confident in saying that Racket has *the* most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a [programmable programming language, a language for building languages][racket-manifesto]. - -This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way *you* want it to look, poking and prodding at a pliable substrate. The sheer *ease* of it all is impossible for me to convey in words, so [you will have to see it for yourself](https://twitter.com/andmkent_/status/724036694773628930). - -All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, [I gave a talk on some of Racket’s linguistic capabilities last year called *Languages in an Afternoon*][languages-in-an-afternoon]. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some [blog posts][tr-adts-with-macros] on this very blog that [demonstrate what Racket can do][envy-intro]. - -The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the `racket/match` module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually *extensible*, so users can add their own pattern-matching forms that cooperate with `match`. - -This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of *minutes*, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box. - -## Fusing Haskell and Racket - -So, let’s assume that we *do* want Haskell’s strong type system and that we *also* want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties: - - 1. First of all, **it’s easy to implement**. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard. - - 2. Unfortunately, there’s a huge problem with this approach: **type information is not available at macroexpansion time**. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet. - -For me, the second option is unacceptable. I am *not* satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale. - -There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented). - -Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called [*Type Systems as Macros*][types-as-macros], which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it. - -The result is [*Rascal*][rascal], a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system. - -# A first peek at Rascal - -Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually *use* it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too. - -First, let me say this up front: **Rascal is probably a lot closer to Haskell than Racket**. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way. - -Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:[^1] - -``` -(def+ map-every-other : (forall [a] {{a -> a} -> (List a) -> (List a)}) - [_ nil -> nil] - [_ {x :: nil} -> {x :: nil}] - [f {x :: y :: ys} -> {x :: (f y) :: (map-every-other f ys)}]) -``` - -This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, *typeclasses*. Yes, with Rascal you can have your monads in all their statically dispatched glory: - -``` -(data (Maybe a) - (just a) - nothing) - -(instance (Monad Maybe) - [join (case-lambda - [(just (just x)) (just x)] - [_ nothing])]) -``` - -So far, though, this really *is* just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that. - -## Core forms can be implemented as derived concepts - -Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros `data` and `case`, in an entirely separate module, which can be imported just like any other library. - -The main `rascal` language provides ADTs by default, of course, but it would be perfectly possible to produce a `rascal/kernel` language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros. - -Simple syntactic transformations are, of course, trivially defined as macros. Haskell `do` notation is defined as [an eleven-line macro in `rascal/monad`][rascal-monad], and GHC’s useful `LambdaCase` extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy. - -While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like `GeneralizedNewtypeDeriving` and other generic deriving mechanisms like `GHC.Generics`, `DeriveGeneric`, and `DeriveAnyClass`. - -## The language is not enough - -No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do *everything*, and it’s impossible to do everything well. - -Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like [servant][servant] let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good. - -Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types: - -``` -type UserAPI = "users" :> Get '[JSON] [User] - :<|> "users" :> ReqBody '[JSON] User :> Post '[JSON] User - :<|> "users" :> Capture "userid" Integer - :> Get '[JSON] User - :<|> "users" :> Capture "userid" Integer - :> ReqBody '[JSON] User - :> Put '[JSON] User -``` - -The above code is *remarkably* readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this: - -``` -(define-api User-API - #:content-types [JSON] - [GET "users" => (List User)] - [POST "users" => User -> User] - [GET "users" [userid : Integer] => User] - [PUT "users" [userid : Integer] => User -> User]) -``` - -This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety). - -In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a *runtime interpreter* for your language! Macros, on the other hand, are compiled, and you get ability to *compile* your DSL to code that can be optimized by all the existing facilities of the compiler toolchain. - -# Rascal is embryonic - -I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to *completely* different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts. - -That said, Rascal doesn’t really exist yet. Yes, [there is a GitHub repository][rascal], and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do *anything* interesting with it, aside from some tiny toy programs. - -As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull. - -Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank [Mark P Jones][mpj] for his wonderful resource [Typing Haskell in Haskell][thih], without which getting to where I am now would likely have been impossible. I also want to thank [Stephen Diehl][stephen-diehl] for his wonderful [Write You a Haskell][wyah] series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet. - -Even with these wonderful resources, I’ve come to the realization that **I probably can’t do all of this on my own**. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance! - -In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of *Type Systems as Macros* for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist. - - -[^1]: Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. - -[envy-intro]: /blog/2015/08/30/managing-application-configuration-with-envy/ -[languages-in-an-afternoon]: https://www.youtube.com/watch?v=TfehOLha-18 -[mpj]: https://web.cecs.pdx.edu/~mpj/ -[racket-manifesto]: http://www.ccs.neu.edu/home/matthias/manifesto/ -[rascal]: https://github.com/lexi-lambda/hackett -[rascal-is-hackett]: /blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/ -[rascal-monad]: https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58 -[servant]: http://hackage.haskell.org/package/servant -[stephen-diehl]: http://www.stephendiehl.com -[thih]: https://web.cecs.pdx.edu/~mpj/thih/ -[tr-adts-with-macros]: /blog/2015/12/21/adts-in-typed-racket-with-macros/ -[types-as-macros]: http://www.ccs.neu.edu/home/stchang/popl2017/ -[wyah]: http://dev.stephendiehl.com/fun/ diff --git a/blog/posts/2017-01-05-rascal-is-now-hackett-plus-some-answers-to-questions.md b/blog/posts/2017-01-05-rascal-is-now-hackett-plus-some-answers-to-questions.md deleted file mode 100644 index 7f05f30..0000000 --- a/blog/posts/2017-01-05-rascal-is-now-hackett-plus-some-answers-to-questions.md +++ /dev/null @@ -1,77 +0,0 @@ - Title: Rascal is now Hackett, plus some answers to questions - Date: 2017-01-05T05:25:32 - Tags: hackett, racket, haskell, programming languages - -Since I published [my blog post introducing Rascal][rascal-intro], I’ve gotten some *amazing* feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that [Rascal is a language that already exists][rascal-mpl]. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, [**Rascal is now known as Hackett**][hackett]. - -With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future. - -# What’s in a name? - -First, a little trivia. - -I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions. - -Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the [Genesis progressive rock guitarist, Steve Hackett][steve-hackett], one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence. - -Perhaps not the most interesting thing in this blog post, but there it is. - -# Why Racket? Why *not* Haskell? - -One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: **Racket is actually two things, a programming language and a programming language platform**. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got. - -Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than `#lang racket`, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that `#lang racket` was ever the more “dominant” language, aside from the name. - -For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, [Languages in an Afternoon][languages-in-an-afternoon], describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing. - -By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free: - - 1. I get a JIT compiler for my code, and I don’t have to implement a compiler myself. - - 2. I also get a package manager that can cooperate with Hackett code to deliver Hackett modules. - - 3. I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor. - - 4. The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk). - - 5. If you don’t want to use DrRacket, you can use the [racket-mode][racket-mode] major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization. - -Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions. - -In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the *Type Systems as Macros* paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander **alone** in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job. - -## Actually running Hackett code - -Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain. - -This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term. - -There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros. - -# How do Template Haskell quasiquoters compete with macros? - -Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition. - -S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has [`syntax/parse`][syntax-parse]), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider [persistent’s quasiquoters][persistent-th]: they look *sort of* like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies. - -Additionally, s-expression macros *compose*, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s `match`, for example, is an expression, and it contains expressions, so `match` can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax. - -Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of. - -Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing *which identifiers are uses and which identifiers are bindings*, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. [Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.](http://i.imgur.com/HvYee19.png) One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be **abstractions**. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid. - -# How can I help? - -Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, [which you can sign up for here][snek-signup]). - -If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful. - -[hackett]: https://github.com/lexi-lambda/hackett -[languages-in-an-afternoon]: https://www.youtube.com/watch?v=TfehOLha-18 -[persistent-th]: http://www.yesodweb.com/book/persistent#persistent_code_generation -[racket-mode]: https://github.com/greghendershott/racket-mode -[rascal-intro]: /blog/2017/01/02/rascal-a-haskell-with-more-parentheses/ -[rascal-mpl]: http://www.rascal-mpl.org -[snek-signup]: http://snek.jneen.net -[steve-hackett]: https://en.wikipedia.org/wiki/Steve_Hackett -[syntax-parse]: http://docs.racket-lang.org/syntax/stxparse.html diff --git a/blog/posts/2017-04-28-lifts-for-free-making-mtl-typeclasses-derivable.md b/blog/posts/2017-04-28-lifts-for-free-making-mtl-typeclasses-derivable.md deleted file mode 100644 index 2c037c6..0000000 --- a/blog/posts/2017-04-28-lifts-for-free-making-mtl-typeclasses-derivable.md +++ /dev/null @@ -1,456 +0,0 @@ - Title: Lifts for free: making mtl typeclasses derivable - Date: 2017-04-28T00:13:19 - Tags: haskell - -Perhaps the most important abstraction a Haskell programmer must understand to effectively write modern Haskell code, beyond the level of the monad, is the *monad transformer*, a way to compose monads together in a limited fashion. One frustrating downside to monad transformers is a proliferation of `lift`s, which explicitly indicate which monad in a transformer “stack” a particular computation should run in. Fortunately, the venerable [mtl][] provides typeclasses that make this lifting mostly automatic, using typeclass machinery to insert `lift` where appropriate. - -Less fortunately, the mtl approach does not actually eliminate `lift` entirely, it simply moves it from use sites to instances. This requires a small zoo of extraordinarily boilerplate-y instances, most of which simply implement each typeclass method using `lift`. While we cannot eliminate the instances entirely without somewhat dangerous techniques like [overlapping instances][overlapping-instances], we *can* automatically derive them using features of modern GHC, eliminating the truly unnecessary boilerplate. - -# The problem with mtl-style typeclasses - -To understand what problem it is exactly that we’re trying to solve, we first need to take a look at an actual mtl-style typeclass. I am going to start with an mtl-*style* typeclass, rather than an actual typeclass in the mtl, due to slight complications with mtl’s actual typeclasses that we’ll get into later. Instead, let’s start with a somewhat boring typeclass, which we’ll call `MonadExit`: - -```haskell -import System.Exit (ExitCode) - -class Monad m => MonadExit m where - exitWith :: ExitCode -> m () -``` - -This is a simple typeclass that abstracts over the concept of early exit, given an exit code. The most obvious implementation of this typeclass is over `IO`, which will actually exit the program: - -```haskell -import qualified System.Exit as IO (exitWith) - -instance MonadExit IO where - exitWith = IO.exitWith -``` - -One of the cool things about these typeclasses, though, is that we don’t have to have just one implementation. We could also write a pure implementation of `MonadExit`, which would simply short-circuit the current computation and return the `ExitCode`: - -```haskell -instance MonadExit (Either ExitCode) where - exitWith = Left -``` - -Instead of simply having an instance on a concrete monad, though, we probably want to be able to use this in a larger monad stack, so we can define an `ExitT` monad transformer that can be inserted into any monad transformer stack: - -```haskell -{-# LANGUAGE GeneralizedNewtypeDeriving #-} - -import Control.Monad.Except (ExceptT, runExceptT, throwError) -import Control.Monad.Trans (MonadTrans) - -newtype ExitT m a = ExitT (ExceptT ExitCode m a) - deriving (Functor, Applicative, Monad, MonadTrans) - -runExitT :: ExitT m a -> m (Either ExitCode a) -runExitT (ExitT x) = runExceptT x - -instance Monad m => MonadExit (ExitT m) where - exitWith = ExitT . throwError -``` - -With this in place, we can write actual programs using our `ExitT` monad transformer: - -```haskell -ghci> runExitT $ do - lift $ putStrLn "hello" - exitWith (ExitFailure 1) - lift $ putStrLn "world" -hello -Left (ExitFailure 1) -``` - -This is pretty cool! Unfortunately, experienced readers will see the rather large problem with what we have so far. Specifically, it won’t actually work if we try and wrap `ExitT` in another monad transformer: - -```haskell -ghci> logIn password = runExitT $ flip runReaderT password $ do - password <- ask - unless (password == "password1234") $ -- super secure password - exitWith (ExitFailure 1) - return "access granted" - -ghci> logIn "not the right password" -: error: - • No instance for (MonadExit (ReaderT [Char] (ExitT m0))) - arising from a use of ‘it’ - • In a stmt of an interactive GHCi command: print it -``` - -The error message is relatively self-explanatory if you are familiar with mtl error messages: there is no `MonadExit` instance for `ReaderT`. This makes sense, since we only defined a `MonadExit` instance for *`ExitT`*, nothing else. Fortunately, the instance for `ReaderT` is completely trivial, since we just need to use `lift` to delegate to the next monad in the stack: - -```haskell -instance MonadExit m => MonadExit (ReaderT r m) where - exitWith = lift . exitWith -``` - -Now that the delegating instance is set up, we can actually use our `logIn` function: - -```haskell -ghci> logIn "not the right password" -Left (ExitFailure 1) -ghci> logIn "password1234" -Right "access granted" -``` - -## An embarrassment of instances - -We’ve managed to make our program work properly now, but we’ve still only defined the delegating instance for `ReaderT`. What if someone wants to use `ExitT` with `WriterT`? Or `StateT`? Or any of `ExceptT`, `RWST`, or `ContT`? Well, we have to define instances for each and every one of them, and as it turns out, the instances are all identical! - -```haskell -instance (MonadExit m, Monoid w) => MonadExit (WriterT w m) where - exitWith = lift . exitWith - -instance MonadExit m => MonadExit (StateT s m) where - exitWith = lift . exitWith - -instance (MonadExit m, Monoid w) => MonadExit (RWST r w s m) where - exitWith = lift . exitWith - -instance MonadExit m => MonadExit (ExceptT e m) where - exitWith = lift . exitWith - -instance MonadExit m => MonadExit (ContT r m) where - exitWith = lift . exitWith -``` - -This is bad enough on its own, but this is actually the *simplest* case: a typeclass with a single method which is trivially lifted through any other monad transformer. Another thing we’ve glossed over is actually defining all the delegating instances for the *other* mtl typeclasses on `ExitT` itself. Fortunately, we can derive these ones with `GeneralizedNewtypeDeriving`, since `ExceptT` has already done most of the work for us: - -```haskell -newtype ExitT m a = ExitT (ExceptT ExitCode m a) - deriving ( Functor, Applicative, Monad, MonadIO -- base - , MonadBase IO -- transformers-base - , MonadTrans, MonadReader r, MonadWriter w, MonadState s -- mtl - , MonadThrow, MonadCatch, MonadMask -- exceptions - , MonadTransControl, MonadBaseControl IO -- monad-control - ) -``` - -Unfortunately, we have to write the `MonadError` instance manually if we want it, since we don’t want to pick up the instance from `ExceptT`, but rather wish to defer to the underlying monad. This means writing some truly horrid delegation code: - -```haskell -instance MonadError e m => MonadError e (ExitT m) where - throwError = lift . throwError - - catchError (ExitT x) f = ExitT . ExceptT $ catchError (runExceptT x) $ \e -> - let (ExitT x') = f e in runExceptT x' -``` - -(Notably, this is so awful because `catchError` is more complex than the simple `exitWith` method we’ve studied so far, which is why we’re starting with a simpler typeclass. We’ll get more into this later, as promised.) - -This huge number of instances is sometimes referred to as the “n2 instances” problem, since it requires every monad transformer have an instance of every single mtl-style typeclass. Fortunately, in practice, this proliferation is often less horrible than it might seem, mostly because deriving helps a lot. However, remember that if `ExitT` *weren’t* a simple wrapper around an existing monad transformer, we wouldn’t be able to derive the instances at all! Instead, we’d have to write them all out by hand, just like we did with all the `MonadExit` instances. - -It’s a shame that these typeclass instances can’t be derived in a more general way, allowing derivation for arbitrary monad transformers instead of simply requiring the newtype deriving machinery. As it turns out, with clever use of modern GHC features, we actually **can**. It’s not even all that hard. - -# Default instances with default signatures - -It’s not hard to see that our `MonadExit` instances are all exactly the same: just `lift . exitWith`. Why is that, though? Well, every instance is an instance on a monad transformer over a monad that is already an instance of `MonadExit`. In fact, we can express this in a type signature, and we can extract `lift . exitWith` into a separate function: - -```haskell -defaultExitWith :: (MonadTrans t, MonadExit m) => ExitCode -> t m () -defaultExitWith = lift . exitWith -``` - -However, writing `defaultExitWith` really isn’t any easier than writing `lift . exitWith`, so this deduplication doesn’t really buy us anything. However, it *does* indicate that we could write a default implementation of `exitWith` if we could require just a little bit more from the implementing type. With [GHC’s `DefaultSignatures` extension][default-signatures], we can do precisely that. - -The idea is that we can write a separate type signature for a default implementation of `exitWith`, which can be more specific than the type signature for `exitWith` in general. This allows us to use our `defaultExitWith` implementation more or less directly: - -```haskell -{-# LANGUAGE DefaultSignatures #-} - -class Monad m => MonadExit m where - exitWith :: ExitCode -> m () - - default exitWith :: (MonadTrans t, MonadExit m1) => ExitCode -> t m1 () - exitWith = lift . exitWith -``` - -We have to use `m1` instead of `m`, since type variables in the instance head are always scoped, and the names would conflict. However, this creates another problem, since our specialized type signature replaces `m` with `t m1`, which won’t quite work (as GHC can’t automatically figure out they should be the same). Instead, we can use `m` in the type signature, then just add a type equality constraint ensuring that `m` and `t m1` must be the same type: - -```haskell -class Monad m => MonadExit m where - exitWith :: ExitCode -> m () - - default exitWith :: (MonadTrans t, MonadExit m1, m ~ t m1) => ExitCode -> m () - exitWith = lift . exitWith -``` - -Now we can write all of our simple instances without even needing to write a real implementation! All of the instance bodies can be empty: - -```haskell -instance MonadExit m => MonadExit (ReaderT r m) -instance (MonadExit m, Monoid w) => MonadExit (WriterT w m) -instance MonadExit m => MonadExit (StateT s m) -instance (MonadExit m, Monoid w) => MonadExit (RWST r w s m) -instance MonadExit m => MonadExit (ExceptT e m) -instance MonadExit m => MonadExit (ContT r m) -``` - -While this doesn’t completely alleviate the pain of writing instances, it’s definitely an improvement over what we had before. With [GHC 8.2’s new `DerivingStrategies` extension][deriving-strategies], it becomes especially beneficial when defining entirely new transformers that should also have `ExitT` instances, since they can be derived with `DeriveAnyClass`: - -```haskell -newtype ParserT m a = ParserT (Text -> m (Maybe (Text, a))) - deriving anyclass (MonadExit) -``` - -This is pretty wonderful. - -Given that only `MonadExit` supports being derived in this way, we sadly still need to implement the other, more standard mtl-style typeclasses ourselves, like `MonadIO`, `MonadBase`, `MonadReader`, `MonadWriter`, etc. However, what if all of those classes provided the same convenient default signatures that our `MonadExit` does? If that were the case, then we could write something like this: - -```haskell -newtype ParserT m a = ParserT (Text -> m (Maybe (Text, a))) - deriving anyclass ( MonadIO, MonadBase b - , MonadReader r, MonadWriter w, MonadState s - , MonadThrow, MonadCatch, MonadMask - , MonadExit - ) -``` - -Compared to having to write all those instances by hand, this would be a pretty enormous difference. Unfortunately, many of these typeclasses are not quite as simple as our `MonadExit`, and we’d have to be a bit more clever to make them derivable. - -# Making mtl’s classes derivable - -Our `MonadExit` class was extremely simple, since it only had a single method with a particularly simple type signature. For reference, this was the type of our generic `exitWith`: - -```haskell -exitWith :: MonadExit m => ExitCode -> m () -``` - -Let’s now turn our attention to `MonadReader`. At first blush, this typeclass should not be any trickier to implement than `MonadExit`, since the types of `ask` and `reader` are both quite simple: - -```haskell -ask :: MonadReader r m => m r -reader :: MonadReader r m => (r -> a) -> m a -``` - -However, the type of the other method, `local`, throws a bit of a wrench in our plans. It has the following type signature: - -```haskell -local :: MonadReader r m => (r -> r) -> m a -> m a -``` - -Why is this so much more complicated? Well, the key is in the second argument, which has the type `m a`. That’s not something that can be simply `lift`ed away! Try it yourself: try to write a `MonadReader` instance for some monad transformer. It’s not as easy as it looks! - -We can illustrate the problem by creating our own version of `MonadReader` and implementing it for something like `ExceptT` ourselves. We can start with the trivial methods first: - -```haskell -class Monad m => MonadReader r m | m -> r where - ask :: m r - local :: (r -> r) -> m a -> m a - reader :: (r -> a) -> m a - -instance MonadReader r m => MonadReader r (ExceptT e m) where - ask = lift ask - reader = lift . reader -``` - -However, implementing `local` is harder. Let’s specialize the type signature to `ExceptT` to make it more clear why: - -```haskell -local :: MonadReader r m => (r -> r) -> ExceptT e m a -> ExceptT e m a -``` - -Our base monad, `m`, implements `local`, but we have to convert the first argument from `ExceptT e m a` into `m (Either e a)` first, run it through `local` in `m`, then wrap it back up in `ExceptT`: - -```haskell -instance MonadReader r m => MonadReader r (ExceptT e m) where - ask = lift ask - reader = lift . reader - local f x = ExceptT $ local f (runExceptT x) -``` - -This operation is actually a mapping operation of sorts, since we’re mapping `local f` over `x`. For that reason, this can be rewritten using the `mapExceptT` function provided from `Control.Monad.Except`: - -```haskell -instance MonadReader r m => MonadReader r (ExceptT e m) where - ask = lift ask - reader = lift . reader - local = mapExceptT . local -``` - -If you implement `MonadReader` instances for other transformers, like `StateT` and `WriterT`, you’ll find that the instances are exactly the same *except* for `mapExceptT`, which is replaced with `mapStateT` and `mapWriterT`, respectively. This is sort of obnoxious, given that we want to figure out how to create a generic version of `local` that works with any monad transformer, but this requires concrete information about which monad we’re in. Obviously, the power `MonadTrans` gives us is not enough to make this generic. Fortunately, there is a typeclass which does: [`MonadTransControl`][MonadTransControl] from the `monad-control` package. - -Using `MonadTransControl`, we can write a generic `mapT` function that maps over an arbitrary monad transformer with a `MonadTransControl` instance: - -```haskell -mapT :: (Monad m, Monad (t m), MonadTransControl t) - => (m (StT t a) -> m (StT t b)) - -> t m a - -> t m b -mapT f x = liftWith (\run -> f (run x)) >>= restoreT . return -``` - -This type signature may look complicated (and, well, it is), but the idea is that the `StT` associated type family encapsulates the monadic state that `t` introduces. For example, for `ExceptT`, `StT (ExceptT e) a` is `Either e a`. For `StateT`, `StT (StateT s) a` is `(a, s)`. Some transformers, like `ReaderT`, have no state, so `StT (ReaderT r) a` is just `a`. - -I will not go into the precise mechanics of how `MonadTransControl` works in this blog post, but it doesn’t matter significantly; the point is that we can now use `mapT` to create a generic implementation of `local` for use with `DefaultSignatures`: - -```haskell -class Monad m => MonadReader r m | m -> r where - ask :: m r - default ask :: (MonadTrans t, MonadReader r m1, m ~ t m1) => m r - ask = lift ask - - local :: (r -> r) -> m a -> m a - default local :: (MonadTransControl t, MonadReader r m1, m ~ t m1) => (r -> r) -> m a -> m a - local = mapT . local - - reader :: (r -> a) -> m a - reader f = f <$> ask -``` - -Once more, we now get instances of our typeclass, in this case `MonadReader`, **for free**: - -```haskell -instance MonadReader r m => MonadReader r (ExceptT e m) -instance (MonadReader r m, Monoid w) => MonadReader r (WriterT w m) -instance MonadReader r m => MonadReader r (StateT s m) -``` - -It’s also worth noting that we *don’t* get a `ContT` instance for free, even though `ContT` has a `MonadReader` instance in mtl. Unlike the other monad transformers mtl provides, `ContT` does not have a `MonadTransControl` instance because it cannot be generally mapped over. While a `mapContT` function does exist, its signature is more restricted: - -```haskell -mapContT :: (m r -> m r) -> ContT k r m a -> ContT k r m a -``` - -It happens that `local` can still be implemented for `ContT`, so it can still have a `MonadReader` instance, but it cannot be derived in the same way as it can for the other transformers. Still, in practice, I’ve found that most user-defined transformers do not have such complex control flow, so they can safely be instances of `MonadTransControl`, and they get this deriving for free. - -## Extending this technique to other mtl typeclasses - -The default instances for the other mtl typeclasses are slightly different from the one for `MonadReader`, but for the most part, the same general technique applies. Here’s a derivable `MonadError`: - -```haskell -class Monad m => MonadError e m | m -> e where - throwError :: e -> m a - default throwError :: (MonadTrans t, MonadError e m1, m ~ t m1) => e -> m a - throwError = lift . throwError - - catchError :: m a -> (e -> m a) -> m a - default catchError :: (MonadTransControl t, MonadError e m1, m ~ t m1) => m a -> (e -> m a) -> m a - catchError x f = liftWith (\run -> catchError (run x) (run . f)) >>= restoreT . return - -instance MonadError e m => MonadError e (ReaderT r m) -instance (MonadError e m, Monoid w) => MonadError e (WriterT w m) -instance MonadError e m => MonadError e (StateT s m) -instance (MonadError e m, Monoid w) => MonadError e (RWST r w s m) -``` - -The `MonadState` interface turns out to be extremely simple, so it doesn’t even need `MonadTransControl` at all: - -```haskell -class Monad m => MonadState s m | m -> s where - get :: m s - default get :: (MonadTrans t, MonadState s m1, m ~ t m1) => m s - get = lift get - - put :: s -> m () - default put :: (MonadTrans t, MonadState s m1, m ~ t m1) => s -> m () - put = lift . put - - state :: (s -> (a, s)) -> m a - state f = do - s <- get - let (a, s') = f s - put s' - return a - -instance MonadState s m => MonadState s (ExceptT e m) -instance MonadState s m => MonadState s (ReaderT r m) -instance (MonadState s m, Monoid w) => MonadState s (WriterT w m) -``` - -Everything seems to be going well! However, not everything is quite so simple. - -## A `MonadWriter` diversion - -Unexpectedly, `MonadWriter` turns out to be by far the trickiest of the bunch. It’s not too hard to create default implementations for most of the methods of the typeclass: - -```haskell -class (Monoid w, Monad m) => MonadWriter w m | m -> w where - writer :: (a, w) -> m a - default writer :: (MonadTrans t, MonadWriter w m1, m ~ t m1) => (a, w) -> m a - writer = lift . writer - - tell :: w -> m () - default tell :: (MonadTrans t, MonadWriter w m1, m ~ t m1) => w -> m () - tell = lift . tell - - listen :: m a -> m (a, w) - default listen :: (MonadTransControl t, MonadWriter w m1, m ~ t m1) => m a -> m (a, w) - listen x = do - (y, w) <- liftWith (\run -> listen (run x)) - y' <- restoreT (return y) - return (y', w) -``` - -However, `MonadWriter` has a fourth method, `pass`, which has a particularly tricky type signature: - -```haskell -pass :: m (a, w -> w) -> m a -``` - -As far as I can tell, this is not possible to generalize using `MonadTransControl` alone, since it would require inspection of the result of the monadic argument (that is, it would require a function from `StT t (a, b) -> (StT t a, b)`), which is not possible in general. My gut is that this could likely also be generalized with a slightly more powerful abstraction than `MonadTransControl`, but it is not immediately obvious to me what that abstraction should be. - -One extremely simple way to make this possible would be to design something to serve this specific use case: - -```haskell -type RunSplit t = forall m a b. Monad m => t m (a, b) -> m (StT t a, Maybe b) -class MonadTransControl t => MonadTransSplit t where - liftWithSplit :: Monad m => (RunSplit t -> m a) -> t m a -``` - -Instances of `MonadTransSplit` would basically just provide a way to pull out bits of the result, if possible: - -```haskell -instance MonadTransSplit (ReaderT r) where - liftWithSplit f = liftWith $ \run -> f (fmap split . run) - where split (x, y) = (x, Just y) - -instance MonadTransSplit (ExceptT e) where - liftWithSplit f = liftWith $ \run -> f (fmap split . run) - where split (Left e) = (Left e, Nothing) - split (Right (x, y)) = (Right x, Just y) - -instance MonadTransSplit (StateT s) where - liftWithSplit f = liftWith $ \run -> f (fmap split . run) - where split ((x, y), s) = ((x, s), Just y) -``` - -Then, using this, it would be possible to write a generic version of `pass`: - -```haskell -default pass :: (MonadTransSplit t, MonadWriter w m1, m ~ t m1) => m (a, w -> w) -> m a -pass m = do - r <- liftWithSplit $ \run -> pass $ run m >>= \case - (x, Just f) -> return (x, f) - (x, Nothing) -> return (x, id) - restoreT (return r) -``` - -However, this seems pretty overkill for just one particular method, given that I have no idea if `MonadTransSplit` would be useful *anywhere* else. One interesting thing about going down this rabbit hole, though, is that I learned that `pass` has some somewhat surprising behavior when mixed with transformers like `ExceptT` or `MaybeT`, if you don’t carefully consider how it works. It’s a strange method with a somewhat strange interface, so I don’t think I have a satisfactory conclusion about `MonadWriter` yet. - -# Regrouping and stepping back - -Alright, that was a lot of fairly intense, potentially confusing code. What the heck did we actually accomplish? Well, we got a couple of things: - - 1. First, we developed a technique for writing simple mtl-style typeclasses that are derivable using `DeriveAnyClass` (or simply writing an empty instance declaration). We used a `MonadExit` class as a proof of concept, but really, the technique is applicable to most mtl-style typeclasses that represent simple effects (including, for example, `MonadIO`). - - This technique is useful in isolation, even if you completely disregard the rest of the blog post. For an example where I recently applied it in real code, see [the default signatures provided with `MonadPersist` from the `monad-persist` library][monad-persist-default-sigs], which make [defining instances completely trivial][monad-persist-instances]. If you use mtl-style typeclasses in your own application to model effects, I don’t see much of a reason *not* to use this technique. - - 2. After `MonadExit`, we applied the same technique to the mtl-provided typeclasses `MonadReader`, `MonadError`, and `MonadState`. These are a bit trickier, since the first two need `MonadTransControl` in addition to the usual `MonadTrans`. - - Whether or not this sort of thing should actually be added to mtl itself probably remains to be seen. For the simplest typeclass, `MonadState`, it seems like there probably aren’t many downsides, but given the difficulty implementing it for `MonadWriter` (or, heaven forbid, `MonadCont`, which I didn’t even seriously take a look at for this blog post), it doesn’t seem like an obvious win. Consistency is important. - - Another downside that I sort of glossed over is possibly even more significant from a practical point of view: adding default signatures to `MonadReader` would require the removal of the default implementation of `ask` that is provided by the existing library (which implements `ask` in terms of `reader`). This would be backwards-incompatible, so it’d be difficult to change, even if people wanted to do it. Still, it’s interesting to consider what these typeclasses might look like if they were designed today. - -Overall, these techniques are not a silver bullet for deriving mtl-style typeclasses, nor do they eliminate the n2 instances problem that mtl style suffers from. That said, they *do* significantly reduce boilerplate and clutter in the simplest cases, and they demonstrate how modern Haskell’s hierarchy of typeclasses provides a lot of power, both to describe quite abstract concepts and to alleviate the need to write code by hand. - -I will continue to experiment with the ideas described in this blog post, and I’m sure some more pros and cons will surface as I explore the design space. If you have any suggestions for how to deal with “the `MonadWriter` problem”, I’d be very interested to hear them! In the meantime, consider using the technique in your application code when writing effectful, monadic typeclasses. - -[default-signatures]: https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#default-method-signatures -[deriving-strategies]: https://downloads.haskell.org/~ghc/8.2.1-rc1/docs/html/users_guide/glasgow_exts.html#deriving-strategies -[MonadTransControl]: http://hackage.haskell.org/package/monad-control-1.0.1.0/docs/Control-Monad-Trans-Control.html#t:MonadTransControl -[monad-persist-default-sigs]: https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L226-L271 -[monad-persist-instances]: https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L506-L513 -[mtl]: https://hackage.haskell.org/package/mtl -[overlapping-instances]: https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#overlapping-instances diff --git a/blog/posts/2017-05-27-realizing-hackett-a-metaprogrammable-haskell.md b/blog/posts/2017-05-27-realizing-hackett-a-metaprogrammable-haskell.md deleted file mode 100644 index d15b615..0000000 --- a/blog/posts/2017-05-27-realizing-hackett-a-metaprogrammable-haskell.md +++ /dev/null @@ -1,225 +0,0 @@ - Title: Realizing Hackett, a metaprogrammable Haskell - Date: 2017-05-27T15:30:00 - Tags: hackett, racket, haskell, programming languages - -[Almost five months ago, I wrote a blog post about my new programming language, Hackett][hackett-intro], a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped. - -Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, [Hackett is not only real, it’s working, and you can try it out yourself][hackett]! - -# A first look at Hackett - -Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour. - -As Racket law decrees it, every Hackett program must begin with `#lang`. We can start with the appropriate incantation: - -```racket -#lang hackett -``` - -If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program: - -```racket -#lang hackett - -(main (println "Hello, world!")) -``` - -In Hackett, a use of `main` at the top level indicates that running the module as a program should execute some `IO` action. In this case, `println` is a function of type `{String -> (IO Unit)}`. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an `IO` value. If you run the above program, you will notice that it really does print out `Hello, world!`, exactly as we would like. - -Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our *own* class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers? - -```racket -#lang hackett - -(def fibs : (List Integer) - {0 :: 1 :: (zip-with + fibs (tail! fibs))}) - -(main (println (show (take 10 fibs)))) -``` - -Again, Hackett is just like Haskell in that it is *lazy*, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call `take`, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day! - -But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably *aren’t* new to programming. How about something more interesting, **like a web server**? - -```racket -#lang hackett - -(require hackett/demo/web-server) - -(data Greeting (greeting String)) - -(instance (->Body Greeting) - [->body (λ [(greeting name)] {"Hello, " ++ name ++ "!"})]) - -(defserver run-server - [GET "/" -> String => "Hello, world!"] - [GET "greet" -> String -> Greeting => greeting]) - -(main (do (println "Running server on port 8080.") - (run-server 8080))) -``` - -```sh -$ racket my-server.rkt -Running server on port 8080. -^Z -$ bg -$ curl 'http://localhost:8080/greet/Alexis' -Hello, Alexis! -``` - -**Welcome to Hackett.** - -# What is Hackett? - -Excited yet? I hope so. I certainly am. - -Before you get a little *too* excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so. - -All that said, it is a *real* tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features: - - - **Algebraic datatypes.** Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes). - - - **Typeclasses.** The demo web server uses a `->Body` typeclass to render server responses, and this module implements a `->Body` instance for the custom `Greeting` datatype. - - - **Macros.** The `defserver` macro provides a concise, readable, *type safe* way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL. - - - **Static typechecking.** Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the `->Body` instance and see what happens. - - - **Infix operators.** In Hackett, `{` curly braces `}` enter *infix mode*, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar. - - - **Pure, monadic I/O.** The `println` and `run-server` functions both produce `(IO Unit)`, and `IO` is a monad. `do` notation is provided as a macro, and it works with any type that implements the `Monad` typeclass. - -All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! **Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.** Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell. - -For a bit more information about what Hackett is and what it aims to be, [check out my blog post from a few months ago][hackett-intro] from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going. - -# The story so far, and getting to Hackett 0.1 - -In September of 2016, I attended [(sixth RacketCon)][sixth-racketcon], where I saw a [pretty incredible and extremely exciting talk][types-as-macros-talk] about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory. - -Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork! - -…it *sort of* worked? - -The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December. - -At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled. - -Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from [Sam Tobin-Hochstadt][samth] and put together [an implementation of Pierce and Turner’s Local Type Inference][local-type-inference-impl]. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) [Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism][complete-and-easy]. After that, things just sort of started falling into place: - - - First, I [implemented the Complete and Easy paper in Haskell][higher-rank], including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing. - - - Three days later, I [ported the Haskell implementation to Racket][higher-rank-racket], using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging. - - - After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to [take a vacation][vacation-tweet] for a week, and with some time to focus, I [souped up the Haskell implementation with products and sums][higher-rank-algebraic]. This was progress! - - - The *following day* I managed to make [similar changes to the Racket implementation][higher-rank-racket-type-constructors], but rather than add anonymous products and sums, I added arbitrary type constructors. - - - A couple days later and with more than a bit of help from [Phil Freeman][phil-freeman], I [rebranded the Racket implementation as Hackett, Mk II][hackett-rebrand], and I started working towards turning it into a real programming language. - -*Less than three weeks later*, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with [editor support][hackett-binding-arrows]. The future of Hackett looks bright, and though there’s a *lot* of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit. - -So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly **no**, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing? - -## What Hackett still *isn’t* - -I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected). - -Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”. - -Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like `Show` and `Eq` is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored. - -Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so `let` and `letrec` need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place. - -Oh, and of course, **the whole thing needs to be documented**. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket. - -All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long. - -# Answering some questions - -It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best. - -## Can I try Hackett? - -Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you *do* want to give it a try, it isn’t difficult: just install Racket, then run `raco pkg install hackett`. Open DrRacket and write `#lang hackett` at the top of the module, then start playing around. - -Also, note that the demo web server used in the example at the top of this blog post is *not* included when you install the `hackett` package. If you want to try that out, you’ll have to run `raco pkg install hackett-demo` to install the demo package as well. - -## Are there any examples of Hackett code? - -Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can [find it on GitHub here][hackett-prim-base], or you can open the `hackett/private/prim/base` module on a local installation. - -## How can I learn more / ask questions about Hackett? - -Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community ([which you can sign up for here][snek]), sending me [a DM on Twitter][my-twitter], opening [an issue on the GitHub repo][hackett-issues], or even just [sending me an email][my-email] (though I’m usually a bit slower to respond to the latter). - -## How can I help? - -Probably the easiest way to help out is to try Hackett for yourself and [report any bugs or infelicities you run into][hackett-issues]. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating. - -If you *are* interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on. - -## How does Hackett compare to *X* / why doesn’t Hackett support *Y*? - -These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance. - -## When will Hackett be ready for me to use? - -I don’t know. - -Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can). - -However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith. - -# Appendix - -It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to [Stephen Chang][stchang], [Joshua Dunfield][joshdunf], [Robby Findler][robby], [Matthew Flatt][mflatt], [Phil Freeman][phil-freeman], [Ben Greenman][ben-greenman], [Alex Knauth][alex-knauth], [Neelakantan Krishnaswami][neelk], and [Sam Tobin-Hochstadt][samth]. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on. - -As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is [on theme with the name][hackett-whats-in-a-name], after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation: - - - The Beach Boys — Pet Sounds - - Boards of Canada — Music Has The Right To Children, Geogaddi - - Bruce Springsteen — Born to Run - - King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline - - Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail - - Mahavishnu Orchestra — Birds of Fire - - Metric — Fantasies, Synthetica, Pagans in Vegas - - Muse — Origin of Symmetry, Absolution, The Resistance - - Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up - - Pink Floyd — Wish You Were Here - - Supertramp — Breakfast In America - - The Protomen — The Protomen, Act II: The Father of Death - - Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light - - Yes — Fragile, Relayer, Going For The One - -And of course, *Voyage of the Acolyte*, by **Steve Hackett**. - -[alex-knauth]: https://github.com/AlexKnauth -[ben-greenman]: http://www.ccs.neu.edu/home/types/ -[complete-and-easy]: http://www.cs.cmu.edu/~joshuad/papers/bidir/ -[hackett]: https://github.com/lexi-lambda/hackett -[hackett-binding-arrows]: https://twitter.com/lexi_lambda/status/867617563206758400 -[hackett-intro]: /blog/2017/01/02/rascal-a-haskell-with-more-parentheses/ -[hackett-issues]: https://github.com/lexi-lambda/hackett/issues -[hackett-prim-base]: https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt -[hackett-rebrand]: https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4 -[hackett-whats-in-a-name]: /blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name -[higher-rank]: https://github.com/lexi-lambda/higher-rank -[higher-rank-algebraic]: https://github.com/lexi-lambda/higher-rank/tree/algebraic -[higher-rank-racket]: https://github.com/lexi-lambda/racket-higher-rank -[higher-rank-racket-type-constructors]: https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors -[joshdunf]: http://www.cs.ubc.ca/~joshdunf/ -[local-type-inference-impl]: https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5 -[mflatt]: http://www.cs.utah.edu/~mflatt/ -[my-email]: mailto:lexi.lambda@gmail.com -[my-twitter]: https://twitter.com/lexi_lambda -[neelk]: http://www.cl.cam.ac.uk/~nk480/ -[phil-freeman]: http://functorial.com -[robby]: http://eecs.northwestern.edu/~robby/ -[samth]: http://www.ccs.neu.edu/home/samth/ -[sixth-racketcon]: http://con.racket-lang.org/2016/ -[snek]: http://snek.jneen.net -[stchang]: http://www.ccs.neu.edu/home/stchang/ -[types-as-macros-talk]: https://www.youtube.com/watch?v=j5Hauz6cewM -[vacation-tweet]: https://twitter.com/lexi_lambda/status/865026650487967744 diff --git a/blog/posts/2017-06-29-unit-testing-effectful-haskell-with-monad-mock.md b/blog/posts/2017-06-29-unit-testing-effectful-haskell-with-monad-mock.md deleted file mode 100644 index d0d0ce6..0000000 --- a/blog/posts/2017-06-29-unit-testing-effectful-haskell-with-monad-mock.md +++ /dev/null @@ -1,280 +0,0 @@ - Title: Unit testing effectful Haskell with monad-mock - Date: 2017-06-29T12:08:49 - Tags: haskell, testing - -Nearly eight months ago, [I wrote a blog post about unit testing effectful Haskell code][using-types-to-unit-test] using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, [monad-mock][]. - -# A first glance at monad-mock - -The monad-mock library is, first and foremost, designed to be *easy*. It doesn’t ask much from you, and it requires almost zero boilerplate. - -The first step is to write an mtl-style interface that encodes an effect you want to mock. For example, you might want to test some code that interacts with the filesystem: - -```haskell -class Monad m => MonadFileSystem m where - readFile :: FilePath -> m String - writeFile :: FilePath -> String -> m () -``` - -Now you just have to write your code as normal. For demonstration purposes, here’s a function that defines copying a file in terms of `readFile` and `writeFile`: - -```haskell -copyFile :: MonadFileSystem m => FilePath -> FilePath -> m () -copyFile a b = do - contents <- readFile a - writeFile b contents -``` - -Making this function work on the real filesystem is trivial, since we just need to define an instance of `MonadFileSystem` for `IO`: - -```haskell -instance MonadFileSystem IO where - readFile = Prelude.readFile - writeFile = Prelude.writeFile -``` - -But how do we test this? Well, we *could* run some real code in `IO`, which might not be so bad for such a simple function, but this seems like a bad idea. For one thing, a bad implementation of `copyFile` could do some pretty horrible things if it misbehaved and decided to overwrite important files, and if you’re constantly running a test suite whenever a file changes, it’s easy to imagine causing a lot of damage. Running tests against the real filesystem also makes tests slower and harder to parallelize, and it only gets much worse once you are doing more complex effects than interacting with the filesystem. - -Using monad-mock, we can test this function in just a couple of lines of code: - -```haskell -import Control.Exception (evaluate) -import Control.Monad.Mock -import Control.Monad.Mock.TH -import Data.Function ((&)) -import Test.Hspec - -makeMock "FileSystemAction" [ts| MonadFileSystem |] - -spec = describe "copyFile" $ - it "reads a file and writes its contents to another file" $ - evaluate $ copyFile "foo.txt" "bar.txt" - & runMock [ ReadFile "foo.txt" :-> "contents" - , WriteFile "bar.txt" "contents" :-> () ] -``` - -That’s it! - -The last two lines of the above snippet are the real interesting bits, which specify the actions that are expected to be executed, and it couples them with their results. You will find that if you tweak the list in any way, such as reordering the actions, eliminating one or both of them, or adding an additional action to the end, the test will fail. We could even turn this into a property-based test that generated arbitrary file paths and file contents. - -Admittedly, in this trivial example, the mock is a little silly, since converting this into a property-based test would demonstrate how much we’ve basically just reimplemented the function in our test. However, once our function starts to do somewhat more complicated things, then our tests become more meaningful. Here’s a similar function that only copies a file if it is nonempty: - -```haskell -copyNonemptyFile :: MonadFileSystem m => FilePath -> FilePath -> m () -copyNonemptyFile a b = do - contents <- readFile a - unless (null contents) $ - writeFile b contents -``` - -This function has some logic which is very clearly *not* expressed in its type, and it would be difficult to encode that information into the type in a safe way. Fortunately, we can guarantee that it works by writing some tests: - -```haskell -describe "copyNonemptyFile" $ do - it "copies a file with contents" $ - evaluate $ copyNonemptyFile "foo.txt" "bar.txt" - & runMock [ ReadFile "foo.txt" :-> "contents" - , WriteFile "bar.txt" "contents" :-> () ] - - it "does nothing with an empty file" $ - evaluate $ copyNonemptyFile "foo.txt" "bar.txt" - & runMock [ ReadFile "foo.txt" :-> "" ] -``` - -These tests are much more useful, and they have some actual value to them. Imagine we had accidentally written `when` instead of `unless`, an easy typo to make. Our tests would fail with some useful error messages: - -``` -1) copyNonemptyFile copies a file with contents - uncaught exception: runMockT: expected the following unexecuted actions to be run: - WriteFile "bar.txt" "contents" - -2) copyNonemptyFile does nothing with an empty file - uncaught exception: runMockT: expected end of program, called writeFile - given action: WriteFile "bar.txt" "" -``` - -You now know enough to write tests with monad-mock. - -# Why unit test? - -When the issue of testing is brought up in Haskell, it is often treated with a certain distaste by a portion of the community. There are some points I’ve seen a number of times, and though they take different forms, they boil down to two ideas: - - 1. “Haskell code does not need tests because the type system can prove correctness.” - - 2. “Testing in Haskell is trivial because it is a pure language, and testing pure functions is easy.” - -I’ve been writing Haskell professionally for over a year now, and I can happily say that there *is* some truth to both of those things! When my Haskell code typechecks, I feel a confidence in it that I would not feel were I using a language with a less powerful type system. Furthermore, Haskell encourages a “pure core, impure shell” approach to system design that makes testing many things pleasant and straightforward, and it completely eliminates the worry of subtle nondeterminism leaking into tests. - -That said, Haskell is not a proof assistant, and its type system cannot guarantee everything, especially for code that operates on the boundaries of what Haskell can control. For much the same reason, I find that my pure code is the code I am *least* likely to need to test, since it is also the code with the strongest type safety guarantees, operating on types in my application’s domain. In contrast, the effectful code is often what I find the most value in extensively testing, since it often contains the most subtle complexity, and it is frequently difficult or even impossible to encode into types. - -Haskell has the power to provide remarkably strong correctness guarantees with a surprisingly small amount of effort by using a combination of tests and types, using each to accommodate for the other’s weaknesses and playing to each technique’s strengths. Some code is test-driven, other code is type-driven. Most code ends up being a mix of both. Testing is just a tool like any other, and it’s nice to feel confident in one’s ability to effectively structure code in a decoupled, testable manner. - -# Why mock? - -Even if you accept that testing is good, the question of whether or not to *mock* is a subtler issue. To some people, “unit testing” is synonymous with mocks. This is emphatically not true, and in fact, overly aggressive mocking is one of the best ways to make your test suite completely worthless. The monad-mock approach to mocking is a bit more principled than mocking in many dynamic, object-oriented languages, but it comes with many of the same drawbacks: mocks couple your tests to your implementation in ways that make them less valuable and less meaningful. - -For the `MonadFileSystem` example above, I would actually probably *not* use a mock. Instead, I would use a **fake**, in-memory filesystem implementation: - -```haskell -newtype FakeFileSystemT m a = FakeFileSystemT (StateT [(FilePath, String)] m a) - deriving (Functor, Applicative, Monad) - -fakeFileSystemT :: Monad m => [(FilePath, String)] - -> FakeFileSystemT m a -> m (a, [(FilePath, String)]) -fakeFileSystemT fs (FakeFileSystemT x) = second sort <$> runStateT x fs - -instance Monad m => MonadFileSystem (FakeFileSystemT m) where - readFile path = FakeFileSystemT $ get >>= \fs -> lookup path fs & - maybe (fail $ "readFile: no such file ‘" ++ path ++ "’") return - writeFile path contents = FakeFileSystemT . modify $ \fs -> - (path, contents) : filter ((/= path) . fst) fs -``` - -The above snippet demonstrates how easy it is to define a `MonadFileSystem` implementation in terms of `StateT`, and while this may seem like a lot of boilerplate, it really isn’t. You have to write a fake *once* per interface, and the above block is a minuscule twelve lines of code. With this technique, you are still able to write tests that depend on the state of the filesystem before and after running the implementation, but you decouple yourself from the precise process of getting there: - -```haskell -describe "copyNonemptyFile" $ do - it "copies a file with contents" $ do - let ((), fs) = runIdentity $ copyNonemptyFile "foo.txt" "bar.txt" - & fakeFileSystemT [ ("foo.txt", "contents") ] - fs `shouldBe` [ ("bar.txt", "contents"), ("foo.txt", "contents") ] - - it "does nothing with an empty file" $ do - let ((), fs) = runIdentity $ copyNonemptyFile "foo.txt" "bar.txt" - & fakeFileSystemT [ ("foo.txt", "") ] - fs `shouldBe` [ ("foo.txt", "") ] -``` - -This is better than using a mock, and I would highly recommend doing it if you can! However, a lot of real applications have to interact with services of much greater complexity than an idealized filesystem, and creating that sort of in-memory fake is not always practical. One such situation might be interacting with AWS CloudFormation, for example: - -```haskell -class Monad m => MonadAWS m where - createStack :: StackName -> StackTemplate -> m (Either AWSError StackId) - listStacks :: m (Either AWSError [StackSummaries]) - describeStack :: StackId -> m (Either AWSError StackInfo) - -- and so on... -``` - -AWS is a very complex system, and it can do dozens of different things (and fail in dozens of different ways) based on an equally complex set of inputs. For example, in the above API, `createStack` needs to parse its template, which can be YAML or JSON, in order to determine which of many possible errors and behaviors can be produced, both on the initial call and on subsequent ones. - -Creating a fake implementation of *AWS* is hardly feasible, and this is where a mock can be useful. By simply writing `makeMock "AWSAction" [ts| MonadAWS |]`, we can test functions that interact with AWS in a pure way without necessarily needing to replicate all of its complexity. - -## Isolating mocks - -Of course, tests that use mocks provide less value than tests that use “smarter” fakes, since they are far more tightly coupled to the implementation, and it’s dramatically more likely that you will need to change the tests when you change the logic. To avoid this, it can be helpful to create multiple interfaces to the same thing: a high-level interface and a low-level one. If our above `MonadAWS` is a low-level interface, we could create a high-level counterpart that does precisely what our application needs: - -```haskell -class Monad m => MonadDeploy m where - executeDeployment :: Deployment -> m (Either DeployError ()) -``` - -When running our application “for real”, we would use `MonadAWS` to implement `MonadDeploy`: - -```haskell -executeDeploymentImpl :: MonadAWS m => Deployment -> m (Either DeployError ()) -executeDeploymentImpl = ... -``` - -The nice thing about this is we can actually test `executeDeploymentImpl` using a `MonadAWS` mock, so we can still have unit test coverage of the code on the boundaries of our system! Additionally, by containing the mock to a single place, we can test the rest of our code using a smarter fake implementation of `MonadDeploy`, helping to decouple our code from AWS’s complex API and improve the reliability and usefulness of our test suite. - -They key point here is that mocking is just a small piece of the larger testing puzzle in *any* language, and that is just as true in Haskell. An overemphasis on mocking is an easy way to end up with a test suite that feels useless, probably because it is. Use mocks as a technique to insulate your application from the complexity in others’ APIs, then use more domain-specific testing techniques and type-level assertions to ensure the correctness of your logic. - -# How monad-mock works - -If you’ve read this far and are convinced that monad-mock is useful, you may safely stop reading now. However, if you are interested in the details of what it actually does and what makes it tick, the rest of this blog post is going to focus on how the implementation works and how it compares to other techniques. - -The centerpiece of monad-mock’s API is its monad transformer, `MockT`, which is a type constructor that accepts three types: - -```haskell -newtype MockT (f :: * -> *) (m :: * -> *) (a :: *) -``` - -The `m` and `a` type variables obviously correspond to the usual monad transformer arguments, which represent the underlying monad and the result of the monadic computation, respectively. The `f` variable is more interesting, since it’s what makes `MockT` work at all, and it isn’t even a type: it’s a type constructor with kind `* -> *`. What does it mean? - -Looking at the type signature of `runMockT` gives us a little bit more information about what that `f` actually represents: - -```haskell -runMockT :: (Action f, Monad m) => [WithResult f] -> MockT f m a -> m a -``` - -This type signature provides two pieces of key information: - - 1. The `f` parameter is constrained by the `Action f` constraint. - - 2. Running a mocked computation requires supplying a list of `WithResult f` values. This list corresponds to the list of expectations provided to `runMock` in earlier examples. - -To understand both of these things, it helps to examine the definition of an actual datatype that can have an `Action` instance. For the filesystem example, the action datatype looks like this: - -```haskell -data FileSystemAction r where - ReadFile :: FilePath -> FileSystemAction String - WriteFile :: FilePath -> String -> FileSystemAction () -``` - -Notice how each constructor clearly corresponds to one of the methods of `MonadFileSystem`, with a type to match. Now the purpose of the type provided to the `FileSystemAction` constructor (in this case `r`) should hopefully become clear: it represents the type of the value *produced* by each method. Also note that the type is completely phantom—it does not appear in negative position in any of the constructors. - -With this in mind, we can take a look at the definition of `WithResult`: - -```haskell -data WithResult f where - (:->) :: f r -> r -> WithResult f -``` - -This is what defines the `(:->)` constructor from earlier in the blog post, and you can see that it effectively just represents a tuple of an action and a value of its associated result. It’s completely type-safe, since it ensures the result matches the type argument to the action. - -Finally, this brings us to the `Action` class, which is not complex, but is unfortunately necessary: - -```haskell -class Action f where - eqAction :: f a -> f b -> Maybe (a :~: b) - showAction :: f a -> String -``` - -Notice that these methods are effectively just `(==)` and `show`, lifted to type constructors of kind `* -> *`. One significant difference is that `eqAction` produces `Maybe (a :~: b)` instead of `Bool`, where `(:~:)` is from `Data.Type.Equality`. This is a type equality witness, which means a successful equality between two values allows the compiler to be sure that the two *types* are equal. This is necessary for the implementation of `runMockT` due to the phantom type in actions—in order to convince GHC that we can properly return the result of a mocked action, we need to assure it that the value we’re going to return is actually of the proper type. - -Implementing this typeclass is not particularly burdensome, but it’s entirely boilerplate, so even if you want to define your own action type (that is, you don’t want to use `makeMock`), you can use the `deriveAction` function from `Control.Monad.Mock.TH` to derive an `Action` instance on an existing datatype. - -## Connecting the mock to its class - -Now that we have an action with which to mock a class, we need to actually define an instance of that class for `MockT`. For this process, monad-mock provides a `mockAction` function with the following type: - -```haskell -mockAction :: (Action f, Monad m) => String -> f r -> MockT f m r -``` - -This function accepts two arguments: the name of the method being mocked and the action that represents the current call. This is easier to illustrate with an actual instance of `MonadFileSystem` using `MockT` and our `FileSystemAction` type: - -```haskell -instance Monad m => MonadFileSystem (MockT FileSystemAction m) where - readFile a = mockAction "readFile" (ReadFile a) - writeFile a b = mockAction "writeFile" (WriteFile a b) -``` - -This allows `readFile` and `writeFile` to defer to the mock, and providing the names of the functions as strings helps monad-mock to produce useful error messages upon failure. Internally, `MockT` is a `StateT` that keeps track of a list of `WithResult f` values as its state. Each call to the mock checks the action against the internal list of calls, and if they match, it returns the associated result. Otherwise, it throws an exception. - -This scheme is simple, but it seems to work remarkably well. There are some obvious enhancements that will probably be eventually necessary, like allowing action results that run in the underlying monad `m` in order to support things like `throwError` from `MonadError`, but so far, it hasn’t been necessary for what we’ve been using it for. Certain tricky signatures defy this simple technique, such as signatures where a monadic action appears in a negative position (that is, the signatures you need things like [monad-control][] or [monad-unlift][] for), but we’ve found that most of our effects don’t have any reason to include such signatures. - -# A brief comparison with free(r) monads - -At this point, astute readers will likely be thinking about free monads, which parts of this technique greatly resemble. The representation of actions as GADTs is especially similar to [freer][], which does something extremely similar. Indeed, you can think of this technique as something that combines a freer-style representation with mtl-style classes. Given that freer already does this, you might ask yourself what the point is. - -If you are already sold on free monads, monad-mock may very well be uninteresting to you. From the perspective of theoretical novelty, monad-mock is not anything new or different. However, there are a variety of practical reasons to prefer mtl over free, and it’s nice to see how easy it is to enjoy the testing benefits of free without too much extra effort. - -An in-depth comparison between mtl and free is well outside the scope of this blog post. However, the key point is that this technique *only* affects test code, so the real runtime implementation will not be affected in any way. This means you can take advantage of the performance benefits and ecosystem support of mtl without sacrificing simple, expressive testing. - -# Conclusion - -To cap things off, I want to emphasize monad-mock’s role as a single part of a larger initiative we’ve been making for the better part of the past eighteen months. Haskell is a language with ever-evolving techniques and style, and it’s sometimes dizzying to figure out how to use all the pieces together to develop robust, maintainable applications. While monad-mock might not be anything drastically different from existing testing techniques, my hope is that it can provide an opinionated mechanism to make testing easy and accessible, even for complex interactions with other services and systems. - -I’ve made an effort to make it abundantly clear in this blog post that monad-mock is *not* a silver bullet to testing, and in fact, I would prefer other techniques for ensuring correctness whenever possible. Even so, mocking is a nice tool to have in your toolbox, and it’s a good fallback to get even the worst APIs under test coverage. - -If you want to try out monad-mock for yourself, [take a look at the documentation on Hackage][monad-mock] and start playing around! It’s still early software, so it’s not the most proven or featureful, but we’ve managed to get mileage out of it already, all the same. If you find any problems, have a use case it does not support, or just find something about it unclear, please do not hesitate to [open an issue on the GitHub repository][monad-mock-repo]—we obviously can’t fix issues we don’t know about. - -Thanks as always to the many people who have contributed ideas that have shaped my philosophy and approach to testing and have helped provide the tools that make this library work. Happy testing! - -[freer]: https://hackage.haskell.org/package/freer -[monad-control]: https://hackage.haskell.org/package/monad-control -[monad-mock]: https://hackage.haskell.org/package/monad-mock -[monad-mock-repo]: https://github.com/cjdev/monad-mock -[monad-unlift]: https://hackage.haskell.org/package/monad-unlift -[using-types-to-unit-test]: /blog/2016/10/03/using-types-to-unit-test-in-haskell/ diff --git a/blog/posts/2017-08-12-user-programmable-infix-operators-in-racket.md b/blog/posts/2017-08-12-user-programmable-infix-operators-in-racket.md deleted file mode 100644 index 018b822..0000000 --- a/blog/posts/2017-08-12-user-programmable-infix-operators-in-racket.md +++ /dev/null @@ -1,389 +0,0 @@ - Title: User-programmable infix operators in Racket - Date: 2017-08-12T16:26:05 - Tags: racket, hackett, macros - -Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in [Hackett][hackett], all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex. - -Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done *without* modifying the stock `#lang racket` reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros. - -# Our mission - -Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we *do* want: - - - Infix operators should be user-extensible, not limited to a special set of built-in operators. - - - Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator. - - - We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like `cons`. This allows `5 - 1 - 2` to be parsed as `(- (- 5 1) 2)`, but `5 :: 1 :: nil` to be parsed as `(:: 5 (:: 1 nil))`. - -These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions: - - - We will **not** permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing `(+ 1 2)` with `{1 + 2}`. - - - Our implementation will **not** support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it. - - - All operators will be binary, and we will **not** support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated. - -With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this: - -```racket -#lang racket - -(require (prefix-in racket/base/ racket/base) - "infix.rkt") - -(define-infix-operator - racket/base/- #:fixity left) -(define-infix-operator :: cons #:fixity right) - -{{2 - 1} :: {10 - 3} :: '()} -; => '(1 7) -``` - -Let’s get started. - -# Implementing infix operators - -Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems: - - 1. How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass? - - 2. How can we associate fixity information with certain operators? - -We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default. - -So, how *do* we detect if a Racket expression is surrounded by curly braces? Normally, in `#lang racket`, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated *exactly* the same as parentheses: - -```racket -> {+ 1 2} -3 -``` - -If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here. - -Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as [*syntax objects*][syntax-object]. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as [*syntax properties*][syntax-property]. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s [*reader*][racket-reader] (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named [`'paren-shape`][paren-shape]. - -This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL: - -```racket -> (syntax-property #'(1 2 3) 'paren-shape) -#f -> (syntax-property #'[1 2 3] 'paren-shape) -#\[ -> (syntax-property #'{1 2 3} 'paren-shape) -#\{ -``` - -This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can. - -## Customizing application - -Racket is a language *designed* to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named [`#%app`][hash-percent-app], which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this: - -```racket -(+ 1 2) -``` - -…into this: - -```racket -(#%app + 1 2) -``` - -What’s special about `#%app` is that the macroexpander will use whichever `#%app` is in scope in the expression’s lexical context, so if we write our own version of `#%app`, it will be used instead of the one from `#lang racket`. This is what we will use to hook into ordinary Racket expressions. - -To write our custom version of `#%app`, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, [`syntax/parse`][syntax-parse]. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the `'paren-shape` syntax property, [`syntax/parse/class/paren-shape`][syntax-classes-paren-shape]. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses: - -```racket -#lang racket - -(require (for-syntax syntax/parse/class/paren-shape) - (prefix-in racket/base/ racket/base) - syntax/parse/define) - -(define-syntax-parser #%app - [{~braces _ arg ...} - #'(#%infix arg ...)] - [(_ arg ...) - #'(racket/base/#%app arg ...)]) -``` - -This code will transform any applications surrounded in curly braces into one that starts with `#%infix` instead of `#%app`, so `{1 + 2}` will become `(#%infix 1 + 2)`, for example. The identifier `#%infix` isn’t actually special in any way, it just has a funny name, but we haven’t actually defined `#%infix` yet, so we need to do that next! - -To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like `{1 + 2}`, should be converted into the equivalent prefix expressions, in this case `(+ 1 2)`. We can do this with a simple macro: - -```racket -(define-syntax-parser #%infix - [(_ a op b) - #'(racket/base/#%app op a b)]) -``` - -Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the `#%app` from `racket/base`, which will avoid any accidental infinite recursion between our `#%app` and `#%infix`. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code: - -```racket -> (+ 1 2) -3 -> {1 + 2} -3 -``` - -That’s pretty cool! - -Of course, we probably want to support infix applications with more than just a single binary operator, such as `{1 + 2 + 3}`. We can implement that just by adding another case to `#%infix` that handles more subforms: - -```racket -(define-syntax-parser #%infix - [(_ a op b) - #'(racket/base/#%app op a b)] - [(_ a op b more ...) - #'(#%infix (#%infix a op b) more ...)]) -``` - -…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators: - -```racket -> {1 + 2 + 3} -6 -> {1 + 2 + 3 + 4} -10 -``` - -I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression: - -```racket -> {1 + 2 * 3 - 4} -5 -``` - -Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as `cons`, nested `->` types or contracts for curried functions, and `expt`, the exponentiation operator. - -## Tracking operator fixity - -Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate. - -Essentially, Racket (like Scheme) uses a `define-syntax` form to define macros, which is what `define-syntax-parser` eventually expands into. However, unlike Scheme, Racket’s `define-syntax` is not *just* for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings: - -```racket -(define-syntax foo 3) -``` - -Once a binding has been defined using `define-syntax`, a macro can look up the value associated with it by using the [`syntax-local-value`][syntax-local-value] function, which returns the compile-time value associated with an identifier: - -```racket -(begin-for-syntax - (println (syntax-local-value #'foo))) -; => 3 -``` - -The cool thing is that `syntax-local-value` gets the value associated with a specific *binding*, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use `syntax-local-value` to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the `foo` binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because `foo` is not bound to anything at runtime. - -To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the [`prop:procedure`][prop-procedure] structure type property. - -If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding: - -```racket -(require (for-syntax syntax/transformer)) - -(begin-for-syntax - (struct infix-operator (runtime-binding fixity) - #:property prop:procedure - (λ (operator stx) - ((set!-transformer-procedure - (make-variable-like-transformer - (infix-operator-runtime-binding operator))) - stx)))) -``` - -This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for `runtime-binding`, but we can also include a value of our choosing for `fixity`. Here’s an example: - -```racket -(define-syntax :: (infix-operator #'cons 'right)) -``` - -This new `::` binding will act, in every way, just like `cons`. If we use it in the REPL, you can see that it acts exactly the same: - -```racket -> (:: 1 '()) -'(1) -``` - -However, we can also use `syntax-local-value` to extract this binding’s fixity at compile-time, and that’s what makes it interesting: - -```racket -(begin-for-syntax - (println (infix-operator-fixity (syntax-local-value #'::)))) -; => 'right -``` - -Using this extra compile-time information, we can adjust our `#%infix` macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used `syntax/parse/class/paren-shape` to make decisions based on the `'paren-shape` syntax property, we can use [`syntax/parse/class/local-value`][syntax-classes-local-value] to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read: - -```racket -(begin-for-syntax - (define-syntax-class infix-op - #:description "infix operator" - #:attributes [fixity] - [pattern {~var op (local-value infix-operator?)} - #:attr fixity (infix-operator-fixity (attribute op.local-value))])) -``` - -Now, we can update `#%infix` to use our new `infix-op` syntax class: - -```racket -(define-syntax-parser #%infix - [(_ a op:infix-op b) - #'(racket/base/#%app op a b)] - [(_ a op:infix-op b more ...) - #:when (eq? 'left (attribute op.fixity)) - #'(#%infix (#%infix a op b) more ...)] - [(_ more ... a op:infix-op b) - #:when (eq? 'right (attribute op.fixity)) - #'(#%infix more ... (#%infix a op b))]) -``` - -Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via `#:when` clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of `#%infix`, we can successfully use `::` in an infix expression, and it will be parsed with the associativity that we expect: - -```racket -> {1 :: 2 :: 3 :: '()} -'(1 2 3) -``` - -Exciting! - -## A nicer interface for defining infix operators - -We currently have to define infix operators by explicitly using `define-syntax`, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the `define-infix-operator` form from the example at the very beginning of this blog post. - -Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the `define-syntax` definitions we were already writing: - -```racket -(define-simple-macro (define-infix-operator op:id value:id - #:fixity {~and fixity {~or {~datum left} {~datum right}}}) - (define-syntax op (infix-operator #'value 'fixity))) -``` - -With this in hand, we can define some infix operators with a much nicer syntax: - -```racket -(define-infix-operator + racket/base/+ #:fixity left) -(define-infix-operator - racket/base/- #:fixity left) -(define-infix-operator * racket/base/* #:fixity left) -(define-infix-operator / racket/base// #:fixity left) - -(define-infix-operator ^ expt #:fixity right) -(define-infix-operator :: cons #:fixity right) -``` - -With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary `#lang racket`: - -```racket -> {1 + 2 - 4} --1 -> {2 ^ 2 ^ 3} -256 -> {{2 ^ 2} ^ 3} -64 -``` - -And you know what’s most amazing about this? The entire thing is **only 50 lines of code**. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted: - -```racket -#lang racket - -(require (for-syntax syntax/parse/class/local-value - syntax/parse/class/paren-shape - syntax/transformer) - (prefix-in racket/base/ racket/base) - syntax/parse/define) - -(begin-for-syntax - (struct infix-operator (runtime-binding fixity) - #:property prop:procedure - (λ (operator stx) - ((set!-transformer-procedure - (make-variable-like-transformer - (infix-operator-runtime-binding operator))) - stx))) - - (define-syntax-class infix-op - #:description "infix operator" - #:attributes [fixity] - [pattern {~var op (local-value infix-operator?)} - #:attr fixity (infix-operator-fixity (attribute op.local-value))])) - -(define-syntax-parser #%app - [{~braces _ arg ...} - #'(#%infix arg ...)] - [(_ arg ...) - #'(racket/base/#%app arg ...)]) - -(define-syntax-parser #%infix - [(_ a op:infix-op b) - #'(racket/base/#%app op a b)] - [(_ a op:infix-op b more ...) - #:when (eq? 'left (attribute op.fixity)) - #'(#%infix (#%infix a op b) more ...)] - [(_ more ... a op:infix-op b) - #:when (eq? 'right (attribute op.fixity)) - #'(#%infix more ... (#%infix a op b))]) - -(define-simple-macro (define-infix-operator op:id value:id - #:fixity {~and fixity {~or {~datum left} {~datum right}}}) - (define-syntax op (infix-operator #'value 'fixity))) - -(define-infix-operator + racket/base/+ #:fixity left) -(define-infix-operator - racket/base/- #:fixity left) -(define-infix-operator * racket/base/* #:fixity left) -(define-infix-operator / racket/base// #:fixity left) - -(define-infix-operator ^ expt #:fixity right) -(define-infix-operator :: cons #:fixity right) -``` - -Racket is a hell of a programming language. - -# Applications, limitations, and implications - -This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am *almost certain* that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even *locally*. - -What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as `let-syntax` and `syntax-parameterize`. Using these tools, it would be entirely possible to implement a `with-fixity` macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make `/` right associative within a block of code: - -```racket -> {1 / 2 / 3} -1/6 -> (with-fixity ([/ right]) - {1 / 2 / 3}) -1 1/2 -``` - -In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of `splicing-let` and `splicing-let-syntax`: - -```racket -(define-simple-macro - (with-fixity ([op:id {~and fixity {~or {~datum left} {~datum right}}}] ...) - body ...) - #:with [op-tmp ...] (generate-temporaries #'[op ...]) - (splicing-let ([op-tmp op] ...) - (splicing-let-syntax ([op (infix-operator #'op-tmp 'fixity)] ...) - body ...))) -``` - -This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations *between* operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy. - -As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in [Hackett][hackett] to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow *any* expression to serve as an infix operator, assuming left associativity if no fixity annotation is available. - -If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at [this file for the definition of infix bindings](https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt), as well as [this file for the defintion of infix application](https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101). My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like `{_ - 1}` to serve as a shorthand for `(lambda [x] {x - 1})`, but I haven’t yet decided if I like the tradeoffs involved. - -It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in `#lang racket` without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so. - -Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to `#lang hackett`, there’s no reason something similar but more powerful couldn’t be built as a separate library by a *user* of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem. - -[hackett]: https://github.com/lexi-lambda/hackett -[hash-percent-app]: http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29 -[paren-shape]: http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29 -[prop-procedure]: http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29 -[racket-reader]: http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29 -[syntax-classes-local-value]: http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29 -[syntax-classes-paren-shape]: http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29 -[syntax-local-value]: http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29 -[syntax-object]: http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29 -[syntax-parse]: http://docs.racket-lang.org/syntax/stxparse.html -[syntax-property]: http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29 diff --git a/blog/posts/2017-08-28-hackett-progress-report-documentation-quality-of-life-and-snake.md b/blog/posts/2017-08-28-hackett-progress-report-documentation-quality-of-life-and-snake.md deleted file mode 100644 index 6444b4c..0000000 --- a/blog/posts/2017-08-28-hackett-progress-report-documentation-quality-of-life-and-snake.md +++ /dev/null @@ -1,360 +0,0 @@ - Title: Hackett progress report: documentation, quality of life, and snake - Date: 2017-08-28T08:00:00 - Tags: hackett, racket, haskell - -Three months ago, [I wrote a blog post describing my new, prototype implementation of my programming language, Hackett][realizing-hackett]. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce [**the first (albeit quite incomplete) approach to Hackett’s documentation**][hackett-doc]. - -I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes. - -# A philosophy of documentation - -Racket, as a project, has always had [wonderful documentation][racket-doc]. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably [Scribble, the Racket documentation tool][scribble]. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with [a powerful library for documenting Racket code][scribble-manual]. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros. - -Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an `@` character instead of `\`. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use [the provided `defproc` form][defproc]: - -```racket -@defproc[(add1 [z number?]) number?]{ -Returns @racket[(+ z 1)].} -``` - -This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into [something like this][racket-add1]: - -[![](/img/scribble-docs-racket-add1.png)][racket-add1] - -The fact that Scribble documents are fully-fledged *programs* equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is [the `scribble/example` module][scribble-example], a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special `eval:check` form that enables [doctest][]-like checking, which allows documentation to serve double duty as a test suite. - -Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is *designed* with the goal of supporting many different programming languages, and Scribble is no exception. Things like [`scribble/example`][scribble-example] essentially work out of the box with Hackett, and most of [`scribble/manual`][scribble-manual] can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The `defproc` and `defstruct` forms are hardly builtins; they are defined as part of the `scribble/manual` library in terms of Scribble primitives, and [we can do the same][hackett-manual]. - -Hackett’s documentation already defines three new forms, `defdata`, `defclass`, and `defmethod`, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the `Functor` typeclass using `defclass` like this: - -```racket -@defclass[(Functor f) - [map : (forall [a b] {(a -> b) -> (f a) -> (f b)})]]{ - -A class of types that are @deftech{functors}, essentially types that provide a -mapping or “piercing” operation. The @racket[map] function can be viewed in -different ways: - -...} -``` - -With only a little more than the above code, [Hackett’s documentation includes a beautifully-typeset definition of the `Functor` typeclass][hackett-functor], including examples and rich prose: - -[![](/img/scribble-docs-hackett-functor.png)][hackett-functor] - -Scribble makes Hackett’s documentation shine. - -## A tale of two users - -For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier. - -A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once. - -However, for experienced users, an authoritative reference is *exactly* what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for. - -This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example: - - - Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs. - - [Java’s API documentation][java-stdlib] documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly *small* language that does not often change. - - On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java *does* provide guide-style documentation in the form of the [Java Tutorials][java-tutorials], but they are of inconsistent quality. - - More importantly, while the Java tutorials link to the API docs, the reverse is **not** true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be. - - Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them. - - - Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages. - - The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is [MDN][mdn]. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions. - - The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN. - - This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. [Ramda’s documentation is nothing but a reference][ramda-docs], which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, [Passport’s docs are essentially *only* a set of tutorials][passport-docs], which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method *does*. Fortunately, [there are some libraries, like React][react-docs], that absolutely *nail* this, and they have both styles of documentation that are **actually cross-referenced**. Unfortunately, those are mostly the exceptions, not the norm. - - - [Python’s documentation is interesting][python-docs], since it includes a set of tutorials alongside the API reference, and it *also* ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on `if` links to the section in the reference about `if`, but nothing goes in the other direction, which is something of a missed opportunity. - - - [Haskell manages to be especially bad here][haskell-base] (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point. - - It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and [Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look][haddock]. - -The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with *two* pieces of documentation: the [Racket Guide][racket-guide] and the [Racket Reference][racket-reference]. The guide includes over **one hundred thousand** words of explanations and examples, and the reference includes roughly **half a million**. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it. - -Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the [Hackett Guide][hackett-guide] and the [Hackett Reference][hackett-reference], and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily *need* to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a *lot* more accessible without making it any less useful for power users. - -# Rounding Hackett’s library, sanding its edges - -One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a *lot* of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool. - -Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary: - - - Hackett includes a [`Double`][hackett-double] type for working with IEEE 754 double-precision floating-point numbers. - - - Local definitions are supported via the [`let`][hackett-let] and [`letrec`][hackett-letrec] forms. - - - The prelude includes many more functions, especially [functions on lists][hackett-lists]. - - - The Hackett reader has been adjusted to support using `.` as a bare symbol, since [`.` is the function composition operator][hackett-composition]. - - - The Hackett REPL supports many more forms, including [ADT][hackett-data], [class][hackett-class], and [instance][hackett-instance] definitions. Additionally, the REPL now uses [`Show`][hackett-show] instances to display the results of expressions. To compensate for the inability to print non-[`Show`][hackett-show]able things, a new `(#:type expr)` syntax is permitted to print the type of *any* expression. - - - Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error. - -Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now [heavily commented](https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189) with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt. - -One of the most exciting things about documenting Hackett has been realizing just *how much* already exists. Seriously, if you have gotten to this point in the blog post but haven’t read [the actual documentation][hackett-doc] yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for *me*: it’s a language I’d like to use. - -Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved! - -# A demo with pictures - -Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we? - -Hackett ships with a special package of demo libraries in the aptly-named `hackett-demo` package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In [the previous Hackett blog post][realizing-hackett], I demonstrated the capabilities of `hackett/demo/web-server`. In this blog post, we’re going to use `hackett/demo/pict` and `hackett/demo/pict/universe`, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code! - -As always, we’ll start with `#lang hackett`, and we’ll import the necessary libraries: - -```racket -#lang hackett - -(require hackett/demo/pict - hackett/demo/pict/universe) -``` - -With that, we can start immediately with a tiny example. Just to see how `hackett/demo/pict` works, let’s start by rendering a red square. We can do this by writing a `main` action that calls `print-pict`: - -```racket -(main (print-pict (colorize red (filled-square 50.0)))) -``` - -If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window! - -![](/img/hackett-pict-red-square.png) - -Using the REPL, we can inspect the type of `print-pict`: - -```racket -> (#:type print-pict) -: (-> Pict (IO Unit)) -``` - -Unsurprisingly, displaying a picture to the screen needs `IO`. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of `filled-square`: - -```racket -> (#:type filled-square) -: (-> Double Pict) -``` - -No `IO` to be seen! This is because “picts” are entirely *pure* values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another: - -```racket -(main (print-pict {(colorize red (filled-square 50.0)) - hc-append - (colorize blue (filled-square 50.0))})) -``` - -This code will print out a red square to the left of a blue one. - -![](/img/hackett-pict-red-blue-squares.png) - -Again, `hc-append` is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one: - -```racket -> (#:type hc-append) -: (-> Pict (-> Pict Pict)) -``` - -Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game! - -## Implementing a snake clone - -This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the `pict` or `universe` libraries. Hopefully it’s still illustrative. - -We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an `Eq` instance for our points. - -```racket -(data Point (point Integer Integer)) -(data Direction d:left d:right d:up d:down) - -(instance (Eq Point) - [== (λ [(point a b) (point c d)] {{a == c} && {b == d}})]) -``` - -With these two datatypes, we can implement a `move` function that accepts a point and a direction and produces a new point for an adjacent tile: - -```racket -(defn move : {Direction -> Point -> Point} - [[d:left (point x y)] (point {x - 1} y)] - [[d:right (point x y)] (point {x + 1} y)] - [[d:up (point x y)] (point x {y - 1})] - [[d:down (point x y)] (point x {y + 1})]) -``` - -The next step is to define a type for our world state. The `big-bang` library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things: - -```racket -(data World-State (world-state - Direction ; snake direction - (List Point) ; snake blocks - (List Point) ; food blocks - )) -``` - -It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax: - -```racket -(defn set-ws-direction [[d (world-state a b c)] (world-state d b c)]) -``` - -Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game: - -```racket -(def board-width 50) -(def board-height 30) -(def tile->absolute {(d* 15.0) . integer->double}) -(def empty-board (blank-rect (tile->absolute board-width) (tile->absolute board-height))) - -(def block (filled-square 13.0)) -(def food-block (colorize red block)) -(def snake-block (colorize black block)) -``` - -Now we can write our actual `render` function. To do this, we simply need to render each `Point` in our `World-State`’s two lists as a block on an `empty-board`. We’ll write a helper function, `render-on-board`, which does exactly that: - -```racket -(defn render-on-board : {Pict -> (List Point) -> Pict} - [[pict points] - (foldr (λ [(point x y) acc] - (pin-over acc (tile->absolute x) (tile->absolute y) pict)) - empty-board points)]) -``` - -This function uses `foldr` to collect each point and place the provided pict at the right location using `pin-over` on an empty board. Using `render-on-board`, we can write the `render` function in just a couple of lines: - -```racket -(defn render : {World-State -> Pict} - [[(world-state _ snake-points food-points)] - (pin-over (render-on-board snake-block snake-points) - 0.0 0.0 - (render-on-board food-block food-points))]) -``` - -Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the `big-bang` library provides a `random-integer` function, which we can use to write a `random-point` action: - -```racket -(def random-point : (IO Point) - {point <$> (random-integer 0 board-width) - <*> (random-integer 0 board-height)}) -``` - -Hackett supports applicative notation using infix operators, so `random-point` looks remarkably readable. It also runs in `IO`, since the result is, obviously, random. Fortunately, the `on-tick` function runs in `IO` as well (unlike `render`, which must be completely pure), so we can use `random-point` when necessary to generate a new food block: - -```racket -(def init! : (forall [a] {(List a) -> (List a)}) - {reverse . tail! . reverse}) - -(defn on-tick : {World-State -> (IO World-State)} - [[(world-state dir snake-points food-points)] - (let ([new-snake-point (move dir (head! snake-points))]) - (if {new-snake-point elem? food-points} - (do [new-food-point <- random-point] - (pure (world-state dir {new-snake-point :: snake-points} - {new-food-point :: (delete new-snake-point food-points)}))) - (pure (world-state dir {new-snake-point :: (init! snake-points)} - food-points))))]) -``` - -This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to `new-snake-point`, then checks if there is a food block at that location. If there is, it generates a `new-food-point`, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual. - -The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our `set-ws-direction` function that we wrote earlier: - -```racket -(defn on-key : {KeyEvent -> World-State -> (IO World-State)} - [[ke:left ] {pure . (set-ws-direction d:left)}] - [[ke:right] {pure . (set-ws-direction d:right)}] - [[ke:up ] {pure . (set-ws-direction d:up)}] - [[ke:down ] {pure . (set-ws-direction d:down)}] - [[_ ] {pure . id}]) -``` - -The `on-key` function runs in `IO`, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in `pure`. - -We’re almost done now—all we need to do is set up the *initial* state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about: - -```racket -(def initial-state - (do [initial-food <- (sequence (take 5 (repeat random-point)))] - (pure (world-state d:right - {(point 25 15) :: (point 24 15) :: (point 23 15) :: nil} - initial-food)))) -``` - -Notably, we can use the `repeat` function to create an infinite list of `random-point` actions, `take` the first five of them, then call `sequence` to execute them from left to right. Now, all we have to do is put the pieces together in a `main` block: - -```racket -(main (do [state <- initial-state] - (big-bang state - #:to-draw render - #:on-tick on-tick 0.2 - #:on-key on-key))) -``` - -And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett. - -![](/img/hackett-snake-animation.gif) - -# Contributing to Hackett - -If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements. - -On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, [Hackett has an issue tracker][hackett-issues], so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals. - -Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to [open an issue on GitHub][hackett-issues], send me a message on the `#racket` IRC channel on Freenode, or ping me on [the Racket Slack team][racket-slack]. - -# Acknowledgements - -Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to [Samuel Gélineau, aka gelisam][gelisam], who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements. - -Additionally, I want to specially thank [Matthew Flatt][mflatt], [Robby Findler][robby], and [Sam Tobin-Hochstadt][samth] for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community. - -Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours. - -[defproc]: http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29 -[doctest]: https://docs.python.org/3/library/doctest.html -[gelisam]: https://github.com/gelisam -[hackett-class]: http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29 -[hackett-composition]: http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29 -[hackett-data]: http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29 -[hackett-doc]: https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/ -[hackett-double]: http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29 -[hackett-functor]: http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29 -[hackett-guide]: http://docs.racket-lang.org/hackett/guide.html -[hackett-instance]: http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29 -[hackett-issues]: https://github.com/lexi-lambda/hackett/issues -[hackett-let]: http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29 -[hackett-letrec]: http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29 -[hackett-lists]: http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29 -[hackett-manual]: https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt -[hackett-reference]: http://docs.racket-lang.org/hackett/reference.html -[hackett-show]: http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29 -[haddock]: https://www.haskell.org/haddock/ -[haskell-base]: https://hackage.haskell.org/package/base -[java-stdlib]: https://docs.oracle.com/javase/8/docs/api/ -[java-tutorials]: https://docs.oracle.com/javase/tutorial/ -[mdn]: https://developer.mozilla.org/en-US/ -[mflatt]: http://www.cs.utah.edu/~mflatt/ -[passport-docs]: http://passportjs.org/docs -[python-docs]: https://docs.python.org/3/index.html -[racket-add1]: http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29 -[racket-doc]: http://docs.racket-lang.org -[racket-guide]: http://docs.racket-lang.org/guide/index.html -[racket-reference]: http://docs.racket-lang.org/reference/index.html -[racket-slack]: http://racket-slack.herokuapp.com -[ramda-docs]: http://ramdajs.com/docs/ -[react-docs]: https://facebook.github.io/react/docs/hello-world.html -[realizing-hackett]: /blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/ -[robby]: http://eecs.northwestern.edu/~robby/ -[samth]: http://www.ccs.neu.edu/home/samth/ -[scribble]: http://docs.racket-lang.org/scribble/index.html -[scribble-example]: http://docs.racket-lang.org/scribble/eval.html -[scribble-manual]: http://docs.racket-lang.org/scribble/plt-manuals.html diff --git a/blog/posts/2017-10-27-a-space-of-their-own-adding-a-type-namespace-to-hackett.md b/blog/posts/2017-10-27-a-space-of-their-own-adding-a-type-namespace-to-hackett.md deleted file mode 100644 index 14ffd33..0000000 --- a/blog/posts/2017-10-27-a-space-of-their-own-adding-a-type-namespace-to-hackett.md +++ /dev/null @@ -1,382 +0,0 @@ - Title: A space of their own: adding a type namespace to Hackett - Date: 2017-10-27T17:41:17 - Tags: hackett, racket, programming languages - -As previously discussed on this blog, [my programming language, Hackett][hackett], is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt? - -For now, at least, the answer is that Hackett will emulate Haskell: **Hackett now has two namespaces**. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working. - -# Why two namespaces? - -Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket. - -This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values: - -```racket -> (map first '((1 a) (2 b) (3 c))) -'(1 2 3) -``` - -In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly *annotate* when they wish to use a value from a different namespace: - -```lisp -> (mapcar #'car '((1 a) (2 b) (3 c))) -(1 2 3) -``` - -The Common Lisp `#'x` reader abbreviation is equivalent to `(function x)`, and `function` is a special form that references a value in the function namespace. - -While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model. - -However, Hackett is a little different from all of the aforementioned languages because Hackett has *types*. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is *always* syntactically unambiguous.[^1] Even if types and values live in separate namespaces, there is no need for a `type` form a la CL’s `function` because it can always be determined implicitly. - -For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following: - -```racket -(data (Tuple a b) (Tuple a b)) -``` - -This defines a binding named `Tuple` at the type level, which is a *type constructor* of two arguments that produces a type of kind `*`,[^2] and another binding named `Tuple` at the value level, which is a *value constructor* of two arguments that produces a value of type `(Tuple a b)`. - -But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor `tuple` instead of `Tuple`? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet: - -```racket -(data Foo bar (baz Integer)) - -(defn foo->integer : {Foo -> Integer} - [[bar ] 0] - [[(baz y)] y]) -``` - -This works fine. But what happens if the programmer decides to change the name of the `bar` value? - -```racket -(data Foo qux (baz Integer)) - -(defn foo->integer : {Foo -> Integer} - [[bar ] 0] - [[(baz y)] y]) -``` - -Can you spot the bug? Disturbingly, this code *still compiles*! Even though `bar` is not a member of `Foo` anymore, it’s still a valid pattern, since names used as patterns match anything, just as the `y` pattern matches against any integer inside the `baz` constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: `(foo->integer (baz 42))` will still produce `0`, not `42`, since the first case always matches. - -Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the `Tuple` example above, which is illegal if a programming language only supports a single namespace. - -Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The `Tuple` example from above is perfectly legal Hackett. - -# Adding namespaces to a language - -Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to *implement* such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace? - -Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: **the remainder of this blog post is *highly technical*, and some of it involves some of the more esoteric corners of Racket’s macro system**. This blog post is *not* representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on. - -## Namespaces as scopes - -With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as [*sets of scopes*][sets-of-scopes], which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier. - -Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the *value scope*, and all identifiers in type position must have a different scope, which we will call the *type scope*. How do we create these scopes and apply them to identifiers? In Racket, we use a function called [`make-syntax-introducer`][make-syntax-introducer], which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can *add* the scope to all pieces of the syntax object, *remove* the scope, or *flip* the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call `make-syntax-introducer` once for each namespace: - -```racket -(begin-for-syntax - (define value-introducer (make-syntax-introducer)) - (define type-introducer (make-syntax-introducer))) -``` - -We define these in a `begin-for-syntax` block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents: - -```racket -(require syntax/parse/define) - -(define-simple-macro (begin/value form ...) - #:with [form* ...] (map (λ (stx) (value-introducer stx 'add)) - (attribute form)) - (begin form* ...)) - -(define-simple-macro (begin/type form ...) - #:with [form* ...] (map (λ (stx) (type-introducer stx 'add)) - (attribute form)) - (begin form* ...)) -``` - -Each of these two forms is like `begin`, which is a Racket form that is, for our purposes, essentially a no-op, but it applies `value-introducer` or `type-introducer` to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces: - -```racket -(begin/value - (define x 'value-x)) - -(begin/type - (define x 'type-x)) - -(begin/value - (println x)) - -(begin/type - (println x)) -``` - -This program produces the following output: - -``` -'value-x -'type-x -``` - -It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces. - -However, although this is close, it isn’t *quite* right. What happens if we nest the two inside each other? - -```racket -(begin/value - (begin/type - (println x))) -``` -``` -x: identifier's binding is ambiguous - context...: - #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) - #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) - #(190359 intdef) - matching binding...: - # - #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) - matching binding...: - # - #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) -``` - -Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of `x` is *ambiguous* because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of `begin/value` or `begin/type` to *override* outer ones, ensuring that a use can only be in a single namespace at a time. - -To do this, we simply need to adjust `begin/value` and `begin/type` to remove the other scope in addition to adding the appropriate one: - -```racket -(define-simple-macro (begin/value form ...) - #:with [form* ...] (map (λ (stx) - (type-introducer (value-introducer stx 'add) 'remove)) - (attribute form)) - (begin form* ...)) - -(define-simple-macro (begin/type form ...) - #:with [form* ...] (map (λ (stx) - (value-introducer (type-introducer stx 'add) 'remove)) - (attribute form)) - (begin form* ...)) -``` - -Now our nested program runs, and it produces `'type-x`, which is exactly what we want—the “nearest” scope wins. - -With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module. - -## Namespaces that cross module boundaries - -The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems. - -Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the *other* module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export *symbols*, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named `foo`, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the `require` form’s job to attach the correct scopes to imported identifiers. - -This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name\![^3] This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary *points* of having multiple namespaces. - -What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s `require` and `provide` forms are extensible, it’s even possible to implement this mangling in a completely invisible way. - -Currently, the scheme that Hackett uses is to prefix `#%hackett-type:` onto the beginning of any type exports. This can be defined in terms of a [*provide pre-transformer*][provide-pre-transformer], which is essentially a macro that cooperates with Racket’s `provide` form to control the export process. In this case, we can define our `type-out` provide pre-transformer in terms of [`prefix-out`][prefix-out], a form built-in to Racket that allows prefixing the names of exports: - -```racket -(define-syntax type-out - (make-provide-pre-transformer - (λ (stx modes) - (syntax-parse stx - [(_ provide-spec ...) - (pre-expand-export - #`(prefix-out #%hackett-type: - #,(type-introducer - #'(combine-out provide-spec ...))) - modes)])))) -``` - -Note that we call `type-introducer` in this macro! That’s because we want to ensure that, when a user writes `(provide (type-out Foo))`, we look for `Foo` in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that `provide` knows *which* `Foo` is being provided. - -Once we have referenced the correct binding, the use of `prefix-out` will appropriately add the `#%hackett-type:` prefix, so the exporting side is already done. Users do need to explicitly write `(type-out ....)` if they are exporting a particular type-level binding, but this is rarely necessary, since most users use `data` or `class` to export datatypes or typeclasses respectively, which can be modified to use `type-out` internally. Very little user code actually needs to change to support this adjustment. - -Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the `#%hackett-type:` prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged. - -Just as we extended `provide` with a provide pre-transformer, we can extend `require` using a [*require transformer*][require-transformer]. In code, this entire process looks like this: - -```racket -(begin-for-syntax - (define (unmangle-type-name name) - (and~> (regexp-match #rx"^#%hackett-type:(.+)$" name) second))) - -(define-syntax unmangle-types-in - (make-require-transformer - (syntax-parser - [(_ require-spec ...) - #:do [(define-values [imports sources] - (expand-import #'(combine-in require-spec ...)))] - (values - (map (match-lambda - [(and i (import local-id src-sym src-mod-path mode req-mode orig-mode orig-stx)) - (let* ([local-name (symbol->string (syntax-e local-id))] - [unmangled-type-name (unmangle-type-name local-name)]) - (if unmangled-type-name - (let* ([unmangled-id - (datum->syntax local-id - (string->symbol unmangled-type-name) - local-id - local-id)]) - (import (type-introducer unmangled-id) - src-sym src-mod-path mode req-mode orig-mode orig-stx)) - i))]) - imports) - sources)]))) -``` - -This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things: - - 1. It iterates over each import and calls `unmangle-type-name` on the imported symbol. If the result is `#f`, that means the import does not have the `#%hackett-type:` prefix, and it can be safely passed through unchanged. - - 2. If `unmangle-type-name` does *not* return `#f`, then it returns the unprefixed name, which is then provided to `datum->syntax`, which allows users to forge new identifiers in an *unhygienic* (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from `unmangle-type-name`, but with the lexical context of the original identifier. - - 3. Finally, we pass the new identifier to `type-introducer` to properly add the type scope, injecting the fresh binding into the type namespace. - -With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write `type-out` when exporting types, it is unlikely that users will want to write `unmangle-types-in` around each and every import in their program. For that reason, we can define a slightly modified version of `require` that implicitly wraps all of its subforms with `unmangle-types-in`: - -```racket -(provide (rename-out [require/unmangle require])) - -(define-simple-macro (require/unmangle require-spec ...) - (require (unmangle-types-in require-spec) ...)) -``` - -…and we’re done. Now, Hackett modules can properly import and export type-level bindings. - -## Namespaces plus submodules: the devil’s in the details - -Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of *submodules*. - -Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file. - -Submodules in Racket come in two flavors: `module` and `module*`. The difference is what order, semantically, they are defined in. Submodules defined with `module` are essentially defined *before* their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with `module*` are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them. - -How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules. - -However, there is [a special sort of `module*` submodule that uses `#f` in place of a module language][guide-submodules], which gives a module access to *all* of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a `test` submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API: - -```racket -#lang racket - -; not provided -(define (private-add1 x) - (+ x 1)) - -(module* test #f - (require rackunit) - (check-equal? (private-add1 41) 42)) -``` - -It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers: - -```racket -(begin-for-syntax - (define value-introducer (make-syntax-introducer)) - (define type-introducer (make-syntax-introducer))) -``` - -Remember that `make-syntax-introducer` is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module [instantiation][module-instantiation], as ensured by Racket’s [separate compilation guarantee][separate-compilation-guarantee]. This means that each module gets its *own* pair of scopes. This means the body of a `module*` submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible. - -Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we *can* preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using [`make-syntax-delta-introducer`][make-syntax-delta-introducer], we can create a syntax introducer the adds or removes the *difference* between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for `value-introducer` and `type-introducer` to always operate on the same scopes on each module instantiation: - -```racket -(define-simple-macro (define-value/type-introducers - value-introducer:id type-introducer:id) - #:with scopeless-id (datum->syntax #f 'introducer-id) - #:with value-id ((make-syntax-introducer) #'scopeless-id) - #:with type-id ((make-syntax-introducer) #'scopeless-id) - (begin-for-syntax - (define value-introducer - (make-syntax-delta-introducer #'value-id #'scopeless-id)) - (define type-introducer - (make-syntax-delta-introducer #'type-id #'scopeless-id)))) - -(define-value/type-introducers value-introducer type-introducer) -``` - -The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. *However*, code inside `begin-for-syntax` blocks is still re-evaluated every time the module is instantiated! This means we are *not* circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result. - -We still use `make-syntax-introducer` to create our two scopes, but critically, we only call `make-syntax-introducer` inside the `define-value/type-introducers` macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds `value-id` and `type-id` as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use `make-syntax-delta-introducer` to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit. - -By guaranteeing each namespace’s scope is always the same, even for different modules, `module*` submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired. - -## The final stretch: making Scribble documentation namespace-aware - -As discussed in [my previous blog post][hackett-documentation-post], Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind. - -In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all! - -Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable. - -To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with `t:` upon import, and they are bound to Scribble [*element transformers*][element-transformer] that indicate they should be typeset without the `t:` prefix. Fortunately, Scribble’s documentation forms *do* understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context. - -In practice, this means Hackett documentation must now include a proliferation of `t:` prefixes. For example, here is the code for a typeset REPL interaction: - -```racket -@(hackett-examples - (defn square : (t:-> t:Integer t:Integer) - [[x] {x * x}]) - (square 5)) -``` - -Note the use of `t:->` and `t:Integer` instead of `->` and `Integer`. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code. - -This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides `deftype` and `deftycon` forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to `t:`-prefixed identifiers to properly index documented forms. Similarly, `defdata` and `defclass` have been updated with an understanding of types. - -The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of `defform` slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into `t:`-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works. - -# Analysis and unsolved problems - -When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging [Matthew Flatt][mflatt] for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be). - -For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom `require` form, types provided by a module’s *language* are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike `require`’s require transformers. - -To circumvent this restriction, `#lang hackett`’s reader includes a somewhat ad-hoc solution that actually inserts a `require` into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case. - -That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do *any* special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully [internalizes extra-linguistic mechanisms][manifesto-extra-linguistic]. - -As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested: - - - **Multi-parameter typeclasses** are implemented, along with **default typeclass method implementations**. - - - Pattern-matching performs basic **exhaustiveness checking**, so unmatched cases are a compile-time error. - - - Hackett ships with a **larger standard library**, including an `Either` type and appropriate functions, an `Identity` type, a `MonadTrans` typeclass, and the `ReaderT` and `ErrorT` monad transformers. - - - **More things are documented**, and parts of the documentation are slightly improved. Additionally, **Hackett’s internals are much more heavily commented**, hopefully making the project more accessible to new contributors. - - - **Parts of the typechecker are dramatically simplified**, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer. - - - As always, various bug fixes. - -Finally, special mention to two new contributors to Hackett, [Milo Turner][iitalics] and [Brendan Murphy][Shamrock-Frost]. Also special thanks to [Matthew Flatt][mflatt] and [Michael Ballantyne][michaelballantyne] for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning. - - -[^1]: “But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have `#lang`. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate `#lang`, not a modified version of Hackett, so Hackett can optimize its user experience for what it *is*, not what it might be someday. - -[^2]: Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow `*` to be used to mean “type” at the kind level and “multiply” at the value level. - -[^3]: This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, *per phase*, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. - - -[element-transformer]: https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29 -[guide-submodules]: https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29 -[hackett]: https://github.com/lexi-lambda/hackett -[hackett-documentation-post]: /blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/ -[make-syntax-delta-introducer]: https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29 -[make-syntax-introducer]: https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29 -[manifesto-extra-linguistic]: http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html -[mflatt]: http://www.cs.utah.edu/~mflatt/ -[module-instantiation]: https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29 -[prefix-out]: https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29 -[provide-pre-transformer]: https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29 -[require-transformer]: https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29 -[separate-compilation-guarantee]: https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29 -[sets-of-scopes]: https://www.cs.utah.edu/plt/scope-sets/ -[iitalics]: https://github.com/iitalics -[Shamrock-Frost]: https://github.com/Shamrock-Frost -[michaelballantyne]: https://github.com/michaelballantyne diff --git a/blog/posts/2018-02-10-an-opinionated-guide-to-haskell-in-2018.md b/blog/posts/2018-02-10-an-opinionated-guide-to-haskell-in-2018.md deleted file mode 100644 index 9e209d6..0000000 --- a/blog/posts/2018-02-10-an-opinionated-guide-to-haskell-in-2018.md +++ /dev/null @@ -1,642 +0,0 @@ - Title: An opinionated guide to Haskell in 2018 - Date: 2018-02-10T08:00:00 - Tags: haskell - -For me, this month marks the end of an era in my life: as of February 2018, I am no longer employed writing Haskell. It’s been a fascinating two years, and while I am excitedly looking forward to what I’ll be doing next, it’s likely I will continue to write Haskell in my spare time. I’ll probably even write it again professionally in the future. - -In the meantime, in the interest of both sharing with others the small amount of wisdom I’ve gained and preserving it for my future self, I’ve decided to write a long, rather dry overview of a few select parts of the Haskell workflow I developed and the ecosystem I settled into. This guide is, as the title notes, *opinionated*—it is what I used in my day-to-day work, nothing more—and I don’t claim that anything here is the only way to write Haskell, nor even the best way. It is merely what I found helpful and productive. Take from it as much or as little as you’d like. - -# Build tools and how to use them - -When it comes to building Haskell, you have options. And frankly, most of them are pretty good. There was a time when `cabal-install` had a (warranted) reputation for being nearly impossible to use and regularly creating dependency hell, but I don’t think that’s the case anymore (though you *do* need to be a little careful about how you use it). Sandboxed builds work alright, and `cabal new-build` and the other `cabal new-*` commands are even better. That said, the UX of `cabal-install` is still less-than-stellar, and it has sharp edges, especially for someone coming from an ecosystem without a heavyweight compilation process like JavaScript, Ruby, or Python. - -Nix is an alternative way to manage Haskell dependencies, and it seems pretty cool. It has a reputation for being large and complicated, and that reputation does not seem especially unfair, but you get lots of benefits if you’re willing to pay the cost. Unfortunately, I have never used it (though I’ve read a lot about it), so I can’t comment much on it here. Perhaps I’ll try to go all-in with Nix when I purchase my next computer, but for now, my workflow works well enough that I don’t feel compelled to switch. - -Personally, I use `stack` as my Haskell build tool. It’s easy to use, it works out of the box, and while it doesn’t enjoy the same amount of caching as `cabal new-build` or Nix, it caches most packages, and it also makes things like git-hosted sources incredibly easy, which (as far as I can tell) can’t be done with `cabal-install` alone. - -This section is going to be a guide on how *I* use `stack`. If you use `cabal-install` with or without Nix, great! Those tools seem good, too. This is not an endorsement of `stack` over the other build tools, just a description of how I use it, the issues I ran into, and my solutions to them. - -## Understanding `stack`’s model and avoiding its biggest gotcha - -Before using `stack`, there are a few things every programmer should know: - - - `stack` is not a package manager, it is a build tool. It does not manage a set of “installed” packages; it simply builds targets and their dependencies. - - - The command to build a target is `stack build `. Just using `stack build` on its own will build the current project’s targets. - - - **You almost certainly do not want to use `stack install`.** - -This is the biggest point of confusion I see among new users of `stack`. After all, when you want to install a package with `npm`, you type `npm install `. So a new Haskeller decides to install `lens`, types `stack install lens`, and then later tries `stack uninstall lens`, only to discover that no such command exists. What happened? - -`stack install` is not like `npm install`. `stack install` is like `make install`. It is nothing more than an alias for `stack build --copy-bins`, and *all* it does is build the target and copy all of its executables into some relatively global location like `~/.local/bin`. This is usually not what you want. - -This design decision is not unique to `stack`; `cabal-install` suffers from it as well. One can argue that it isn’t unintuitive because it really is just following what `make install` conventionally does, and the fact that it happens to conflict with things like `npm install` or even `apt-get install` is just a naming clash. I think that argument is a poor one, however, and I think the decision to even include a `stack install` command was a bad idea. - -So, remember: don’t use `stack install`! `stack` works best when everything lives inside the current project’s *local* sandbox, and `stack install` copies executables into a *global* location by design. While it might sometimes appear to work, it’s almost always wrong. The *only* situation in which `stack install` is the right answer is when you want to install an executable for a use unrelated to Haskell development (that is, something like `pandoc`) that just so happens to be provided by a Haskell package. **This means no running `stack install ghc-mod` or `stack install intero` either, no matter what READMEs might tell you!** Don’t worry: I’ll cover the proper way to install those things later. - -## Actually building your project with `stack` - -Okay, so now that you know to never use `stack install`, what *do* you use? Well, `stack build` is probably all you need. Let’s cover some variations of `stack build` that I use most frequently. - -Once you have a `stack` project, you can build it by simply running `stack build` within the project directory. However, for local development, this is usually unnecessarily slow because it runs the GHC optimizer. For faster development build times, pass the `--fast` flag to disable optimizations: - -``` -$ stack build --fast -``` - -By default, `stack` builds dependencies with coarse-grained, package-level parallelism, but you can enable more fine-grained, module-level parallel builds by adding `--ghc-options=-j`. Unfortunately, there are conflicting accounts on whether or not this actually makes things faster or slower in practice, and I haven’t extensively tested to see whether or not this is the case, so I mostly leave it off. - -Usually, you also want to build and run the tests along with your code, which you can enable with the `--test` flag. Additionally, `stack test` is an alias for `stack build --test`, so these two commands are equivalent: - -``` -$ stack build --fast --test -$ stack test --fast -``` - -Also, it is useful to build documentation as well as code! You can do this by passing the `--haddock` flag, but unfortunately, I find Haddock sometimes takes an unreasonably long time to run. Therefore, since I usually only care about running Haddock on my dependencies, I usually pass the `--haddock-deps` flag instead, which prevents having to re-run Haddock every time you build: - -``` -$ stack test --fast --haddock-deps -``` - -Finally, I usually want to build and test my project in the background whenever my code changes. Fortunately, this can be done easily by using the `--file-watch` flag, making it easy to incrementally change project code and immediately see results: - -``` -$ stack test --fast --haddock-deps --file-watch -``` - -This is the command I usually use to develop my Haskell projects. - -## Accessing local documentation - -While Haskell does not always excel on the documentation front, a small amount of documentation is almost always better than no documentation at all, and I find my dependencies’ documentation to be an invaluable resource while developing. I find many people just look at docs on Hackage or use the hosted instance of Hoogle, but this sometimes leads people astray: they might end up looking at the wrong version of the documentation! Fortunately, there’s an easy solution to this problem, which is to browse the documentation `stack` installs locally, which is guaranteed to match the version you are using in your current project. - -The easiest way to open local documentation for a particular package is to use the `stack haddock --open` command. For example, to open the documentation for `lens`, you could use the following command: - -``` -$ stack haddock --open lens -``` - -This will open the local documentation in your web browser, and you can browse it at your leisure. If you have already built the documentation using the `--haddock-deps` option I recommended in the previous section, this command should complete almost instantly, but if you haven’t built the documentation yet, you’ll have to wait as `stack` builds it for you on-demand. - -While this is a good start, it isn’t perfect. Ideally, I want to have *searchable* documentation, and fortunately, this is possible to do by running Hoogle locally. This is easy enough with modern versions of `stack`, which have built-in Hoogle integration, but it still requires a little bit of per-project setup, since you need to build the Hoogle search index with the following command: - -``` -$ stack hoogle -- generate --local -``` - -This will install Hoogle into the current project if it isn’t already installed, and it will index your dependencies’ documentation and generate a new Hoogle database. Once you’ve done that, you can start a web server that serves a local Hoogle search page with the following command: - -``` -$ stack hoogle -- server --local --port=8080 -``` - -Navigate to `http://localhost:8080` in your web browser, and you’ll have a fully-searchable index of all your Haskell packages’ documentation. Isn’t that neat? - -Unfortunately, you *will* have to manually regenerate the Hoogle database when you install new packages and their documentation, which you can do by re-running `stack hoogle -- generate --local`. Fortunately, regenerating the database doesn’t take very long, as long as you’ve been properly rebuilding the documentation with `--haddock-deps`. - -## Configuring your project - -Every project built with `stack` is configured with two separate files: - - - The `stack.yaml` file, which controls which packages are built and what versions to pin your dependencies to. - - - The `.cabal` file *or* `package.yaml` file, which specifies build targets, their dependencies, and which GHC options to apply, among other things. - -The `.cabal` file is, ultimately, what is used to build your project, but modern versions of `stack` generate projects that use hpack, which uses an alternate configuration file, the `package.yaml` file, to generate the `.cabal` file. This can get a little bit confusing, since it means you have *three* configuration files in your project, one of which is generated from the other one. - -I happen to use and like hpack, so I use a `package.yaml` file and allow hpack to generate the `.cabal` file. I have no real love for YAML, and in fact I think custom configuration formats are completely fine, but the primary advantage of hpack is the ability to specify things like GHC options and default language extensions for all targets at once, instead of needing to duplicate them per-target. - -You can think of the `.cabal` or `package.yaml` file as a specification for *how* your project is built and *what packages* it depends on, but the `stack.yaml` file is a specification of precisely *which version* of each package should be used and where it should be fetched from. Also, each `.cabal` file corresponds to precisely *one* Haskell package (though it may have any number of executable targets), but a `stack.yaml` file can specify multiple different packages to build, useful for multi-project builds that share a common library. The details here can be a little confusing, more than I am likely going to be able to explain in this blog post, but for the most part, you can get away with the defaults unless you’re doing something fancy. - -## Setting up editor integration - -Currently, I use Atom to write Haskell. Atom is not a perfect editor by any means, and it leaves a lot to be desired, but it’s easy to set up, and the Haskell editor integration is decent. - -Atom’s editor integration is powered by `ghc-mod`, a program that uses the GHC API to provide tools to inspect Haskell programs. Installing `ghc-mod` must be done manually so that Atom’s `haskell-ghc-mod` package can find it, and this is where a lot of people get tripped up. They run `stack install ghc-mod`, it installs `ghc-mod` into `~/.local/bin`, they put that in their `PATH`, and things work! …except when a new version of GHC is released a few months later, everything stops working. - -As mentioned above, **`stack install` is not what you want**. Tools like `ghc-mod`, `hlint`, `hoogle`, `weeder`, and `intero` work best when installed as part of the sandbox, *not* globally, since that ensures they will match the current GHC version your project is using. This can be done per-project using the ordinary `stack build` command, so the easiest way to properly install `ghc-mod` into a `stack` project is with the following command: - -``` -$ stack build ghc-mod -``` - -Unfortunately, this means you will need to run that command inside every single `stack` project individually in order to properly set it up so that `stack exec -- ghc-mod` will find the correct executable. One way to circumvent this is by using a recently-added `stack` flag designed for this explicit purpose, `--copy-compiler-tool`. This is like `--copy-bins`, but it copies the executables into a *compiler-specific location*, so a tool built for GHC 8.0.2 will be stored separately from the same tool built for GHC 8.2.2. `stack exec` arranges for the executables for the current compiler version to end up in the `PATH`, so you only need to build and install your tools once per compiler version. - -Does this kind of suck? Yes, a little bit, but it sucks a whole lot less than all your editor integration breaking every time you switch to a project that uses a different version of GHC. I use the following command in a fresh sandbox when a Stackage LTS comes out for a new version of GHC: - -``` -$ stack build --copy-compiler-tool ghc-mod hoogle weeder -``` - -This way, I only have to build those tools once, and I don’t worry about rebuilding them again until a the next release of GHC. To verify that things are working properly, you should be able to create a fresh `stack` project, run a command like this one, and get a similar result: - -``` -$ stack exec -- which ghc-mod -/Users/alexis/.stack/compiler-tools/x86_64-osx/ghc-8.2.2/bin/ghc-mod -``` - -Note that this path is scoped to my operating system and my compiler version, but nothing else—no LTS or anything like that. - -# Warning flags for a safe build - -Haskell is a relatively strict language as programming languages go, but in my experience, it isn’t quite strict enough. Many things are not errors that probably ought to be, like orphan instances and inexhaustive pattern matches. Fortunately, GHC provides *warnings* that catch these problems statically, which fill in the gaps. I recommend using the following flags on all projects to ensure everything is caught: - - - [`-Wall`][-Wall] - - [`-Wcompat`][-Wcompat] - - [`-Wincomplete-record-updates`][-Wincomplete-record-updates] - - [`-Wincomplete-uni-patterns`][-Wincomplete-uni-patterns] - - [`-Wredundant-constraints`][-Wredundant-constraints] - -The [`-Wall`][-Wall] option turns on *most* warnings, but (ironically) not all of them. The [`-Weverything`][-Weverything] flag truly turns on *all* warnings, but some of the warnings left disabled by [`-Wall`][-Wall] really are quite silly, like warning when type signatures on polymorphic local bindings are omitted. Some of them, however, are legitimately useful, so I recommend turning them on explicitly. - -[`-Wcompat`][-Wcompat] enables warnings that make your code more robust in the face of future backwards-incompatible changes. These warnings are trivial to fix and serve as free future-proofing, so I see no reason not to turn these warnings on. - -[`-Wincomplete-record-updates`][-Wincomplete-record-updates] and [`-Wincomplete-uni-patterns`][-Wincomplete-uni-patterns] are things I think ought to be enabled by [`-Wall`][-Wall] because they both catch what are essentially partial pattern-matches (and therefore runtime errors waiting to happen). The fact that [`-Wincomplete-uni-patterns`][-Wincomplete-uni-patterns] *isn’t* enabled by [`-Wall`][-Wall] is so surprising that it can lead to bugs being overlooked, since the extremely similar [`-Wincomplete-patterns`][-Wincomplete-patterns] *is* enabled by [`-Wall`][-Wall]. - -[`-Wredundant-constraints`][-Wredundant-constraints] is a useful warning that helps to eliminate unnecessary typeclass constraints on functions, which can sometimes occur if a constraint was previously necessary but ends up becoming redundant due to a change in the function’s behavior. - -I put all five of these flags in the `.cabal` file (or `package.yaml`), which enables them everywhere, but this alone is unlikely to enforce a warning-free codebase, since the build will still succeed even in the presence of warnings. Therefore, when building projects in CI, I pass the [`-Werror`][-Werror] flag (using `--ghc-options=-Werror` for `stack`), which treats warnings as errors and halts the build if any warnings are found. This is useful, since it means warnings don’t halt the whole build while developing, making it possible to write some code that has warnings and still run the test suite, but it still enforces that pushed code be warning-free. - -# Any flavor you like - -Haskell is both a language and a spectrum of languages. It is both a standard and a specific implementation. Haskell 98 and Haskell 2010 are good, small languages, and there are a few different implementations, but when people talk about “Haskell”, unqualified, they’re almost always talking about GHC. - -GHC Haskell, in stark contrast to standard Haskell, is neither small nor particularly specific, since GHC ships with *dozens* of knobs and switches that can be used to configure the language. In theory, this is a little terrifying. How could anyone ever hope to talk about Haskell and agree upon how to write it if there are so many *different* Haskells, each a little bit distinct? Having a cohesive ecosystem would be completely hopeless. - -Fortunately, in practice, this is not nearly as bad as it seems. The majority of GHC extensions are simple switches: a feature is either on or it is off. Turning a feature on rarely affects code that does not use it, so most extensions can be turned on by default, and programmers may simply avoid the features they do not wish to use, just as any programmer in any programming language likely picks a subset of their language’s features to use on a daily basis. Writing Haskell is not different in this regard, only in the sense that it does not allow all features to be used by default; everything from minor syntactic tweaks to entirely new facets of the type system are opt-in. - -Frankly, I think the UX around this is terrible. I recognize the desire to implement a standard Haskell, and the old `-fglasgow-exts` was not an especially elegant solution for people wishing to use nonstandard Haskell, but having to insert `LANGUAGE` pragmas at the top of every module just to take advantage of the best features GHC has to offer is a burden, and it is unnecessarily intimidating. I think much of the Haskell community finds the use of `LANGUAGE` pragmas preferable to enabling extensions globally using the `default-extensions` list in the `.cabal` file, but I cut across the grain on that issue *hard*. The vast majority of language extensions I use are extensions I want enabled all the time; a list of them at the top of a module is just distracting noise, and it only serves to bury the extensions I really do want to enable on a module-by-module basis. It also makes it tricky to communicate with a team which extensions are acceptable (or even preferable) and which are discouraged. - -My ***strong*** recommendation if you decide to write GHC Haskell on a team is to agree as a group to a list of extensions the team is happy with enabling everywhere and putting those extensions in the `default-extensions` list in the `.cabal` file. This eliminates clutter, busywork, and the conceptual overhead of remembering which extensions are in favor, and which are discouraged. This is a net win, and it isn’t at all difficult to look in the `.cabal` file when you want to know which extensions are in use. - -Now, with that small digression out of the way, the question becomes precisely which extensions should go into that `default-extensions` list. I happen to like using most of the features GHC makes available, so I enable a whopping **34** language extensions *by default*. As of GHC 8.2, here is my list: - - - [`ApplicativeDo`][ApplicativeDo] - - [`BangPatterns`][BangPatterns] - - [`ConstraintKinds`][ConstraintKinds] - - [`DataKinds`][DataKinds] - - [`DefaultSignatures`][DefaultSignatures] - - [`DeriveFoldable`][DeriveFoldable] - - [`DeriveFunctor`][DeriveFunctor] - - [`DeriveGeneric`][DeriveGeneric] - - [`DeriveLift`][DeriveLift] - - [`DeriveTraversable`][DeriveTraversable] - - [`DerivingStrategies`][DerivingStrategies] - - [`EmptyCase`][EmptyCase] - - [`ExistentialQuantification`][ExistentialQuantification] - - [`FlexibleContexts`][FlexibleContexts] - - [`FlexibleInstances`][FlexibleInstances] - - [`FunctionalDependencies`][FunctionalDependencies] - - [`GADTs`][GADTs] - - [`GeneralizedNewtypeDeriving`][GeneralizedNewtypeDeriving] - - [`InstanceSigs`][InstanceSigs] - - [`KindSignatures`][KindSignatures] - - [`LambdaCase`][LambdaCase] - - [`MultiParamTypeClasses`][MultiParamTypeClasses] - - [`MultiWayIf`][MultiWayIf] - - [`NamedFieldPuns`][NamedFieldPuns] - - [`OverloadedStrings`][OverloadedStrings] - - [`PatternSynonyms`][PatternSynonyms] - - [`RankNTypes`][RankNTypes] - - [`ScopedTypeVariables`][ScopedTypeVariables] - - [`StandaloneDeriving`][StandaloneDeriving] - - [`TupleSections`][TupleSections] - - [`TypeApplications`][TypeApplications] - - [`TypeFamilies`][TypeFamilies] - - [`TypeFamilyDependencies`][TypeFamilyDependencies] - - [`TypeOperators`][TypeOperators] - -This is a lot, and a few of them are likely to be more controversial than others. Since I do not imagine everyone will agree with everything in this list, I’ve broken it down into smaller chunks, arranged from what I think ought to be least controversial to most controversial, along with a little bit of justification why each extension is in each category. If you’re interested in coming up with your own list of extensions, the rest of this section is for you. - -## Trivial lifting of standards-imposed limitations - -A few extensions are tiny changes that lift limitations that really have no reason to exist, other than that they are mandated by the standard. I am not sure why these restrictions are in the standard to begin with, other than perhaps a misguided attempt at making the language simpler. These extensions include the following: - - - [`EmptyCase`][EmptyCase] - - [`FlexibleContexts`][FlexibleContexts] - - [`FlexibleInstances`][FlexibleInstances] - - [`InstanceSigs`][InstanceSigs] - - [`MultiParamTypeClasses`][MultiParamTypeClasses] - -These extensions have no business *not* being turned on everywhere. [`FlexibleContexts`][FlexibleContexts] and [`FlexibleInstances`][FlexibleInstances] end up being turned on in almost any nontrivial Haskell module, since without them, the typeclass system is pointlessly and artificially limited. - -[`InstanceSigs`][InstanceSigs] is extremely useful, completely safe, and has zero downsides. - -[`MultiParamTypeClasses`][MultiParamTypeClasses] are almost impossible to avoid, given how many libraries use them, and they are a completely obvious generalization of single-parameter typeclasses. Much like [`FlexibleContexts`][FlexibleContexts] and [`FlexibleInstances`][FlexibleInstances], I see no real reason to ever leave these disabled. - -[`EmptyCase`][EmptyCase] is even stranger to me, since [`EmptyDataDecls`][EmptyDataDecls] is in Haskell 2010, so it’s possible to define empty datatypes in standard Haskell but not exhaustively pattern-match on them! This is silly, and [`EmptyCase`][EmptyCase] should be standard Haskell. - -## Syntactic conveniences - -A few GHC extensions are little more than trivial, syntactic abbreviations. These things would be tiny macros in a Lisp, but they need to be extensions to the compiler in Haskell: - - - [`LambdaCase`][LambdaCase] - - [`MultiWayIf`][MultiWayIf] - - [`NamedFieldPuns`][NamedFieldPuns] - - [`TupleSections`][TupleSections] - -All of these extensions are only triggered by explicit use of new syntax, so existing programs will never change behavior when these extensions are introduced. - -[`LambdaCase`][LambdaCase] only saves a few characters, but it eliminates the need to come up with a fresh, unique variable name that will only be used once, which is sometimes hard to do and leads to worse names overall. Sometimes, it really is better to leave something unnamed. - -[`MultiWayIf`][MultiWayIf] isn’t something I find I commonly need, but when I do, it’s nice to have. It’s far easier to read than nested `if...then...else` chains, and it uses the existing guard syntax already used with function declarations and `case...of`, so it’s easy to understand, even to those unfamiliar with the extension. - -[`NamedFieldPuns`][NamedFieldPuns] avoids headaches and clutter when using Haskell records without the [accidental identifier capture issues][RecordWildCards-accidental-identifier-capture] of [`RecordWildCards`][RecordWildCards]. It’s a nice, safe compromise that brings some of the benefits of [`RecordWildCards`][RecordWildCards] without any downsides. - -[`TupleSections`][TupleSections] is a logical generalization of tuple syntax in the same vein as standard operator sections, and it’s quite useful when using applicative notation. I don’t see any reason to not enable it. - -## Extensions to the deriving mechanism - -GHC’s typeclass deriving mechanism is one of the things that makes Haskell so pleasant to write, and in fact I think Haskell would be nearly unpalatable to write without it. Boilerplate generation is a good thing, since it defines operations in terms of a single source of truth, and generated code is code you do not need to maintain. There is rarely any reason to write a typeclass instance by hand when the deriving mechanism will write it automatically. - -These extensions give GHC’s typeclass deriving mechanism more power without any cost. Therefore, I see no reason *not* to enable them: - - - [`DeriveFoldable`][DeriveFoldable] - - [`DeriveFunctor`][DeriveFunctor] - - [`DeriveGeneric`][DeriveGeneric] - - [`DeriveLift`][DeriveLift] - - [`DeriveTraversable`][DeriveTraversable] - - [`DerivingStrategies`][DerivingStrategies] - - [`GeneralizedNewtypeDeriving`][GeneralizedNewtypeDeriving] - - [`StandaloneDeriving`][StandaloneDeriving] - -The first five of these simply extend the list of typeclasses GHC knows how to derive, something that will only ever be triggered if the user explicitly requests GHC derive one of those classes. [`GeneralizedNewtypeDeriving`][GeneralizedNewtypeDeriving] is quite possibly one of the most important extensions in all of Haskell, since it dramatically improves `newtype`s’ utility. Wrapper types can inherit instances they need without any boilerplate, and making increased type safety easier and more accessible is always a good thing in my book. - -[`DerivingStrategies`][DerivingStrategies] is new to GHC 8.2, but it finally presents the functionality of GHC’s [`DeriveAnyClass`][DeriveAnyClass] extension in a useful way. [`DeriveAnyClass`][DeriveAnyClass] is useful when used with certain libraries that use [`DefaultSignatures`][DefaultSignatures] (discussed later) with `GHC.Generics` to derive instances of classes without the deriving being baked into GHC. Unfortunately, enabling [`DeriveAnyClass`][DeriveAnyClass] essentially disables the far more useful [`GeneralizedNewtypeDeriving`][GeneralizedNewtypeDeriving], so I do *not* recommend enabling [`DeriveAnyClass`][DeriveAnyClass]. Fortunately, with [`DerivingStrategies`][DerivingStrategies], it’s possible to opt into the `anyclass` deriving strategy on a case-by-case basis, getting some nice boilerplate reduction in the process. - -[`StandaloneDeriving`][StandaloneDeriving] is useful when GHC’s deriving algorithms aren’t *quite* clever enough to deduce the instance context automatically, so it allows specifying it manually. This is only useful in a few small situations, but it’s nice to have, and there are no downsides to enabling it, so it ought to be turned on. - -## Lightweight syntactic adjustments - -A couple extensions tweak Haskell’s syntax in more substantial ways than things like [`LambdaCase`][LambdaCase], but not in a significant enough way for them to really be at all surprising: - - - [`BangPatterns`][BangPatterns] - - [`KindSignatures`][KindSignatures] - - [`TypeOperators`][TypeOperators] - -[`BangPatterns`][BangPatterns] mirror strictness annotations on datatypes, so they are unlikely to be confusing, and they provide a much more pleasant notation for annotating the strictness of bindings than explicit uses of `seq`. - -[`KindSignatures`][KindSignatures] are also fairly self-explanatory: they’re just like type annotations, but for types instead of values. Writing kind signatures explicitly is usually unnecessary, but they can be helpful for clarity or for annotating phantom types when [`PolyKinds`][PolyKinds] is not enabled. Enabling [`KindSignatures`][KindSignatures] doesn’t have any adverse effects, so I see no reason not to enable it everywhere. - -[`TypeOperators`][TypeOperators] adjusts the syntax of types slightly, allowing operators to be used as type constructors and written infix, which is technically backwards-incompatible, but I’m a little suspicious of anyone using `(!@#$)` as a type variable (especially since standard Haskell does not allow them to be written infix). This extension is useful with some libraries like `natural-transformations` that provide infix type constructors, and it makes the type language more consistent with the value language. - -## Polymorphic string literals - -I’m putting this extension in a category all of its own, mostly because I don’t think any other Haskell extensions have quite the same set of tradeoffs: - - - [`OverloadedStrings`][OverloadedStrings] - -For me, [`OverloadedStrings`][OverloadedStrings] is not optional. Haskell’s infamous “string problem” (discussed in more detail at the end of this blog post) means that `String` is a linked list of characters, and all code that cares about performance actually uses `Text`. Manually invoking `pack` on every single string literal in a program is just noise, and [`OverloadedStrings`][OverloadedStrings] solves that noise. - -That said, I actually find I don’t use the polymorphism of string literals very often, and I’d be alright with monomorphic literals if I could make them *all* have type `Text`. Unfortunately, there isn’t a way to do this, so [`OverloadedStrings`][OverloadedStrings] is the next best thing, even if it sometimes causes some unnecessary ambiguities that require type annotations to resolve. - -[`OverloadedStrings`][OverloadedStrings] is an extension that I use so frequently, in so many modules (especially in my test suites) that I would rather keep it on everywhere so I don’t have to care about whether or not it’s enabled in the module I’m currently writing. On the other hand, it certainly isn’t my favorite language extension, either. I wouldn’t go as far as to call it a necessary evil, since I don’t think it’s truly “evil”, but it does seem to be necessary. - -## Simple extensions to aid type annotation - -The following two extensions significantly round out Haskell’s language for referring to types, making it much easier to insert type annotations where necessary (for removing ambiguity or for debugging type errors): - - - [`ScopedTypeVariables`][ScopedTypeVariables] - - [`TypeApplications`][TypeApplications] - -That the behavior of [`ScopedTypeVariables`][ScopedTypeVariables] is *not* the default is actually one of the most common gotchas for new Haskellers. Sadly, it can theoretically adjust the behavior of existing Haskell programs, so I cannot include it in the list of trivial changes, but I would argue such programs were probably confusing to begin with, and I have never seen a program in practice that was impacted by that problem. I think leaving [`ScopedTypeVariables`][ScopedTypeVariables] off is much, much more likely to be confusing than turning it on. - -[`TypeApplications`][TypeApplications] is largely unrelated, but I include it in this category because it’s quite useful and cooperates well with [`ScopedTypeVariables`][ScopedTypeVariables]. Use of [`TypeApplications`][TypeApplications] makes instantiation much more lightweight than full-blown type annotations, and once again, it has no downsides if it is enabled and unused (since it is a syntactic addition). I recommend enabling it. - -## Simple extensions to the Haskell type system - -A few extensions tweak the Haskell type system in ways that I think are simple enough to be self-explanatory, even to people who might not have known they existed. These are as follows: - - - [`ConstraintKinds`][ConstraintKinds] - - [`RankNTypes`][RankNTypes] - -[`ConstraintKinds`][ConstraintKinds] is largely just used to define typeclass aliases, which is both useful and self-explanatory. Unifying the type and constraint language also has the effect of allowing type-level programming with constraints, which is sometimes useful, but far rarer in practice than the aforementioned use case. - -[`RankNTypes`][RankNTypes] are uncommon, looking at the average type in a Haskell program, but they’re certainly nice to have when you need them. The idea of pushing `forall`s further into a type to adjust how variables are quantified is something that I find people find fairly intuitive, especially after seeing them used once or twice, and higher-rank types do crop up regularly, if infrequently. - -## Intermediate syntactic adjustments - -Three syntactic extensions to Haskell are a little bit more advanced than the ones I’ve already covered, and none of them are especially related: - - - [`ApplicativeDo`][ApplicativeDo] - - [`DefaultSignatures`][DefaultSignatures] - - [`PatternSynonyms`][PatternSynonyms] - -[`ApplicativeDo`][ApplicativeDo] is, on the surface, simple. It changes `do` notation to use `Applicative` operations where possible, which allows using `do` notation with applicative functors that are not monads, and it also makes operations potentially more performant when `(<*>)` can be implemented more efficiently than `(>>=)`. In theory, it sounds like there are no downsides to enabling this everywhere. However, there are are a few drawbacks that lead me to put it so low on this list: - - 1. It considerably complicates the desugaring of `do` blocks, to the point where the algorithm cannot even be easily syntactically documented. In fact, an additional compiler flag, `-foptimal-applicative-do`, is a way to *opt into* optimal solutions for `do` block expansions, tweaking the desugaring algorithm to have an *O*(*n*3) time complexity! This means that the default behavior is guided by a heuristic, and desugaring isn’t even especially predictable. This isn’t necessarily so bad, since it’s really only intended as an optimization when some `Monad` operations are still necessary, but it does dramatically increase the complexity of one of Haskell’s core forms. - - 2. The desugaring, despite being *O*(*n*2) by default, isn’t even especially clever. It relies on a rather disgusting hack that recognizes `return e`, `return $ e`, `pure e`, or `pure $ e` expressions *syntactically*, and it completely gives up if an expression with precisely that shape is not the final statement in a `do` block. This is a bit awkward, since it effectively turns `return` and `pure` into syntax when before they were merely functions, but that isn’t all. It also means that the following `do` block is *not* desugared using `Applicative` operations: - - ```haskell - do foo a b - bar s t - baz y z - ``` - - This will use the normal, monadic desugaring, despite the fact that it is trivially desugared into `Applicative` operations as `foo a b *> bar s t *> baz y z`. In order to get [`ApplicativeDo`][ApplicativeDo] to trigger here, the `do` block must be contorted into the following: - - ```haskell - do foo a b - bar s t - r <- baz y z - pure r - ``` - - This seems like an odd oversight. - - 3. [`TemplateHaskell`][TemplateHaskell] doesn’t seem able to cope with `do` blocks when [`ApplicativeDo`][ApplicativeDo] is enabled. I reported this as [an issue on the GHC bug tracker][ghc-trac-14471], but it hasn’t received any attention, so it’s not likely to get fixed unless someone takes the initiative to do so. - - 4. Enabling [`ApplicativeDo`][ApplicativeDo] can cause problems with code that may have assumed `do` would always be monadic, and sometimes, that can cause code that typechecks to lead to an infinite loop at runtime. Specifically, if `do` notation is used to define `(<*>)` in terms of `(>>=)`, enabling [`ApplicativeDo`][ApplicativeDo] will cause the definition of `(<*>)` to become self-referential and therefore divergent. Fortunately, this issue can be easily mitigated by simply writing `(<*>) = ap` instead, which is clearer and shorter than the equivalent code using `do`. - -Given all these things, it seems [`ApplicativeDo`][ApplicativeDo] is a little too new in a few places, and it isn’t quite baked. Still, I keep it enabled by default. Why? Well, *usually* it works fine without any problems, and when I run into issues, I can disable it on a per-module basis by writing `{-# LANGUAGE NoApplicativeDo #-}`. I still find that keeping it enabled by default is fine the vast majority of the time, I just sometimes need to work around the bugs. - -In contrast, [`DefaultSignatures`][DefaultSignatures] isn’t buggy at all, as far as I can tell, it’s just not usually useful without fairly advanced features like [`GADTs`][GADTs] (for type equalities) or `GHC.Generics`. I mostly use it for [making lifting instances for `mtl`-style typeclasses easier to write][lifts-for-free], which I’ve found to be a tiny bit tricky to explain (mostly due to the use of type equalities in the context), but it works well. I don’t see any real reason to leave this disabled, but if you don’t think you’re going to use it anyway, it doesn’t really matter one way or the other. - -Finally, [`PatternSynonyms`][PatternSynonyms] allow users to extend the pattern language just as they are allowed to extend the value language. Bidirectional pattern synonyms are isomorphisms, and it’s quite useful to allow those isomorphisms to be used with Haskell’s usual pattern-matching syntax. I think this extension is actually quite benign, but I put it so low on this list because it seems infrequently used, and I get the sense most people consider it fairly advanced. I would argue, however, that it’s a very pleasant, useful extension, and it’s no more complicated than a number of the features in Haskell 98. - -## Intermediate extensions to the Haskell type system - -Now we’re getting into the meat of things. Everything up to this point has been, in my opinion, completely self-evident in its usefulness and simplicity. As far as I’m concerned, the extensions in the previous six sections have no business ever being left disabled. Starting in this section, however, I could imagine a valid argument being made either way. - -The following three extensions add some complexity to the Haskell type system in return for some added expressive power: - - - [`ExistentialQuantification`][ExistentialQuantification] - - [`FunctionalDependencies`][FunctionalDependencies] - - [`GADTs`][GADTs] - -[`ExistentialQuantification`][ExistentialQuantification] and [`GADTs`][GADTs] are related, given that the former is subsumed by the latter, but [`GADTs`][GADTs] also enables an alternative syntax. Both syntaxes allow packing away a typeclass dictionary or equality constraint that is brought into scope upon a successful pattern-match against a data constructor, something that is sometimes quite useful but certainly a departure from Haskell’s simple ADTs. - -[`FunctionalDependencies`][FunctionalDependencies] extend multi-parameter typeclasses, and they are almost unavoidable, given their use in the venerable `mtl` library. Like [`GADTs`][GADTs], [`FunctionalDependencies`][FunctionalDependencies] add an additional layer of complexity to the typeclass system in order to express certain things that would otherwise be difficult or impossible. - -All of these extensions involve a tradeoff. Enabling [`GADTs`][GADTs] also implies [`MonoLocalBinds`][MonoLocalBinds], which disables let generalization, one of the most likely ways a program that used to typecheck might subsequently fail to do so. Some might argue that this is a good reason to turn [`GADTs`][GADTs] on in a per-module basis, but I disagree: I actually want my language to be fairly consistent, and given that I know I am likely going to want to use [`GADTs`][GADTs] *somewhere*, I want [`MonoLocalBinds`][MonoLocalBinds] enabled *everywhere*, not inconsistently and sporadically. - -That aside, all these extensions are relatively safe. They are well-understood, and they are fairly self-contained extensions to the Haskell type system. I think these extensions have a very good power to cost ratio, and I find myself using them regularly (especially [`FunctionalDependencies`][FunctionalDependencies]), so I keep them enabled globally. - -## Advanced extensions to the Haskell type system - -Finally, we arrive at the last set of extensions in this list. These are the most advanced features Haskell’s type system currently has to offer, and they are likely to be the most controversial to enable globally: - - - [`DataKinds`][DataKinds] - - [`TypeFamilies`][TypeFamilies] - - [`TypeFamilyDependencies`][TypeFamilyDependencies] - -All of these extensions exist exclusively for the purpose of type-level programming. [`DataKinds`][DataKinds] allows datatype promotion, creating types that are always uninhabited and therefore can only be used phantom. [`TypeFamilies`][TypeFamilies] allows the definition of type-level functions that map types to other types. Both of these are minor extensions to Haskell’s surface area, but they have rather significant ramifications on the sort of programming that can be done and the way GHC’s typechecker must operate. - -[`TypeFamilies`][TypeFamilies] is an interesting extension because it comes in so many flavors: associated type synonyms, associated datatypes, open and closed type synonym families, and open and closed datatype families. Associated types tend to be easier to grok and easier to use, though they can also be replaced by functional dependencies. Open type families are also quite similar to classes and instances, so they aren’t *too* tricky to understand. Closed type families, on the other hand, are a rather different beast, and they can be used to do fairly advanced things, *especially* in combination with [`DataKinds`][DataKinds]. - -I happen to appreciate GHC’s support for these features, and while I’m hopeful that an eventual `DependentHaskell` will alleviate many of the existing infelicities with dependently typed programming in GHC, in the meantime, it’s often useful to enjoy what exists where practically applicable. Therefore, I have little problem keeping them enabled, since, like the vast majority of extensions on this list, these extensions merely lift restrictions, not adjust semantics of the language without the extensions enabled. When I am going to write a type family, I am going to turn on [`TypeFamilies`][TypeFamilies]; I see no reason to annotate the modules in which I decide to do so. I do not write an annotation at the top of each module in which I define a typeclass or a datatype, so why should I do so with type families? - -[`TypeFamilyDependencies`][TypeFamilyDependencies] is a little bit different, since it’s a very new extension, and it doesn’t seem to always work as well as I would hope. Still, when it doesn’t work, it fails with a very straightforward error message, and when it works, it is legitimately useful, so I don’t see any real reason to leave it off if [`TypeFamilies`][TypeFamilies] is enabled. - -## Extensions intentionally left off this list - -Given what I’ve said so far, it may seem like I would advocate flipping on absolutely every lever GHC has to offer, but that isn’t actually true. There are a few extensions I quite intentionally do *not* enable. - -[`UndecidableInstances`][UndecidableInstances] is something I turn on semi-frequently, since GHC’s termination heuristic is not terribly advanced, but I turn it on per-module, since it’s useful to know when it’s necessary (and in application code, it rarely is). [`OverlappingInstances`][OverlappingInstances] and [`IncoherentInstances`][IncoherentInstances], in contrast, are completely banned—not only are they almost always a bad idea, GHC has a better, more fine-grained way to opt into overlapping instances, using the `{-# OVERLAPPING #-}`, `{-# OVERLAPPABLE #-}`, and `{-# INCOHERENT #-}` pragmas. - -[`TemplateHaskell`][TemplateHaskell] and [`QuasiQuotes`][QuasiQuotes] are tricky ones. Anecdotes seem to suggest that enabling [`TemplateHaskell`][TemplateHaskell] everywhere leads to worse compile times, but after trying this on a few projects and measuring, I wasn’t able to detect any meaningful difference. Unless I manage to come up with some evidence that these extensions actually slow down compile times just by being *enabled*, even if they aren’t used, then I may add them to my list of globally-enabled extensions, since I use them regularly. - -Other extensions I haven’t mentioned are probably things I just don’t use very often and therefore haven’t felt the need to include on this list. It certainly isn’t exhaustive, and I add to it all the time, so I expect I will continue to do so in the future. This is just what I have for now, and if your favorite extension isn’t included, it probably isn’t a negative judgement against that extension. I just didn’t think to mention it. - -# Libraries: a field guide - -Now that you’re able to build a Haskell project and have chosen which handpicked flavor of Haskell you are going to write, it’s time to decide which libraries to use. Haskell is an expressive programming language, and the degree to which different libraries can shape the way you structure your code is significant. Picking the right libraries can lead to clean code that’s easy to understand and maintain, but picking the wrong ones can lead to disaster. - -Of course, there are *thousands* of Haskell libraries on Hackage alone, so I cannot hope to cover all of the ones I have ever found useful, and I certainly cannot cover ones that would be useful but I did not have the opportunity to try (of which there are certainly many). This blog post is long enough already, so I’ll just cover a few categories of libraries that I think I can offer interesting commentary on; most libraries can generally speak for themselves. - -## Having an effect - -One of the first questions Haskell programmers bump into when they begin working on a large application is how they’re going to model effects. Few practical programming languages are pure, but Haskell is one of them, so there’s no getting away from coming up with a way to manage side-effects. - -For some applications, Haskell’s built-in solution might be enough: `IO`. This can work decently for data processing programs that do very minimal amounts of I/O, and the types of side-effects they perform are minimal. For these applications, most of the logic is likely to be pure, which means it’s already easy to reason about and easy to test. For other things, like web applications, it’s more likely that a majority of the program logic is going to be side-effectful by its nature—it may involve making HTTP requests to other services, interacting with a database, and writing to logfiles. - -Figuring out how to structure these effects in a type-safe, decoupled, composable way can be tricky, especially since Haskell has so many different solutions. I could not bring myself to choose just one, but I did choose two: the so-called “`mtl` style” and freer monads. - -`mtl` style is so named because it is inspired by the technique of interlocking monadic typeclasses and lifting instances used to model effects using constraints that is used in the [`mtl`][mtl] library. Here is a small code example of what `mtl` style typeclasses and handlers look like: - -```haskell -class Monad m => MonadFileSystem m where - readFile :: FilePath -> m String - writeFile :: FilePath -> String -> m () - - default readFile :: (MonadTrans t, MonadFileSystem m', m ~ t m') => FilePath -> m String - readFile a = lift $ readFile a - - default writeFile :: (MonadTrans t, MonadFileSystem m', m ~ t m') => FilePath -> String -> m () - writeFile a b = lift $ writeFile a b - -instance MonadFileSystem IO where - readFile = Prelude.readFile - writeFile = Prelude.writeFile - -instance MonadFileSystem m => MonadFileSystem (ExceptT e m) -instance MonadFileSystem m => MonadFileSystem (MaybeT m) -instance MonadFileSystem m => MonadFileSystem (ReaderT r m) -instance MonadFileSystem m => MonadFileSystem (StateT s m) -instance MonadFileSystem m => MonadFileSystem (WriterT w m) - -newtype InMemoryFileSystemT m a = InMemoryFileSystemT (StateT [(FilePath, String)] m a) - deriving (Functor, Applicative, Monad, MonadError e, MonadReader r, MonadWriter w) - -instance Monad m => MonadFileSystem (InMemoryFileSystemT m) where - readFile path = InMemoryFileSystemT $ do - vfs <- get - case lookup path vfs of - Just contents -> pure contents - Nothing -> error ("readFile: no such file " ++ path) - - writeFile path contents = InMemoryFileSystemT $ modify $ \vfs -> - (path, contents) : delete (path, contents) vfs -``` - -This is the most prevalent way to abstract over effects in Haskell, and it’s been around for a long time. Due to the way it uses the typeclass system, it’s also very fast, since GHC can often specialize and inline the typeclass dictionaries to avoid runtime dictionary passing. The main drawbacks are the amount of boilerplate required and the conceptual difficulty of understanding exactly how monad transformers, monadic typeclasses, and lifting instances all work together to discharge `mtl` style constraints. - -There are various alternatives to `mtl`’s direct approach to effect composition, most of which are built around the idea of reifying a computation as a data structure and subsequently interpreting it. The most popular of these is the `Free` monad, a clever technique for deriving a monad from a functor that happens to be useful for modeling programs. Personally, I think `Free` is overhyped. It’s a cute, mathematically elegant technique, but it involves a lot of boilerplate, and composing effect algebras is still a laborious process. The additional expressive power of `Free`, namely its ability to choose an interpreter dynamically, at runtime, is rarely necessary or useful, and it adds complexity and reduces performance for few benefits. (And in fact, this is still possible to do with `mtl` style, it’s just uncommon because there is rarely any need to do so.) - -A 2017 blog post entitled [Free monad considered harmful][free-monad-considered-harmful] discussed `Free` in comparison with `mtl` style, and unsurprisingly cast `Free` in a rather unflattering light. I largely agree with everything outlined in that blog post, so I will not retread its arguments here. I do, however, think that there is another abstraction that *is* quite useful: the so-called “freer monad” used to implement extensible effects. - -Freer moves even further away from worrying about functors and monads, since its effect algebras do not even need to be functors. Instead, freer’s effect algebras are ordinary GADTs, and reusable, composable effect handlers are easily written to consume elements of these datatypes. Unfortunately, the way this works means that GHC is still not clever enough to optimize freer monads as efficiently as `mtl` style, since it can’t easily detect when the interpreter is chosen statically and use that information to specialize and inline effect implementations, but the cost difference is significantly reduced, and I’ve found that in real application code, the vast majority of the cost does not come from the extra overhead introduced by a more expensive `(>>=)`. - -There are a few different implementations of freer monads, but I, sadly, was not satisfied with any of them, so I decided to contribute to the problem by creating yet another one. My implementation is called [`freer-simple`][freer-simple], and it includes a streamlined API with [more documentation than any other freer implementation][freer-simple-documentation]. Writing the above `mtl` style example using `freer-simple` is more straightforward: - -```haskell -data FileSystem r where - ReadFile :: FilePath -> FileSystem String - WriteFile :: FilePath -> String -> FileSystem () - -readFile :: Member FileSystem r => FilePath -> Eff r String -readFile a = send $ ReadFile a - -writeFile :: Member FileSystem r => FilePath -> String -> Eff r () -writeFile a b = send $ WriteFile a b - -runFileSystemIO :: LastMember IO r => Eff (FileSystem ': r) ~> Eff r -runFileSystemIO = interpretM $ \case - ReadFile a -> Prelude.readFile a - WriteFile a b -> Prelude.writeFile a b - -runFileSystemInMemory :: [(FilePath, String)] -> Eff (FileSystem ': effs) ~> Eff effs -runFileSystemInMemory initVfs = runState initVfs . fsToState where - fsToState :: Eff (FileSystem ': effs) ~> Eff (State [(FilePath, String)] ': effs) - fsToState = reinterpret $ case - ReadFile path -> get >>= \vfs -> case lookup path vfs of - Just contents -> pure contents - Nothing -> error ("readFile: no such file " ++ path) - WriteFile path contents -> modify $ \vfs -> - (path, contents) : delete (path, contents) vfs -``` - -(It could be simplified further with a little bit of Template Haskell to generate the `readFile` and `writeFile` function definitions, but I haven’t gotten around to writing that.) - -So which effect system do I recommend? I used to recommend `mtl` style, but as of only two months ago, I now recommend `freer-simple`. It’s easier to understand, involves less boilerplate, achieves “good enough” performance, and generally gets out of the way wherever possible. Its API is designed to make it easy to do the sorts of the things you most commonly need to do, and it provides a core set of effects that can be used to build a real-world application. - -That said, freer is indisputably relatively new and relatively untested. It has success stories, but `mtl` style is still the approach used by the majority of the ecosystem. `mtl` style has more library support, its performance characteristics are better understood, and it is a tried and true way to structure effects in a Haskell application. If you understand it well enough to use it, and you are happy with it in your application, my recommendation is to stick with it. If you find it confusing, however, or you end up running up against its limits, give `freer-simple` a try. - -## Through the looking glass: to lens or not to lens - -There’s no getting around it: [`lens`][lens] is a behemoth of a library. For a long time, I wrote Haskell without it, and honestly, it worked out alright. I just wasn’t doing a whole lot of work that involved complicated, deeply-nested data structures, and I didn’t feel the need to bring in a library with such a reputation for having impenetrable operators and an almost equally impenetrable learning curve. - -But, after some time, I decided I wanted to take the plunge. So I braced myself for the worst, pulled out my notebook, and started writing some code. To my surprise… it wasn’t that hard. It made sense. Sure, I still don’t know how it works on the inside, and I never did learn the majority of the exports in `Control.Lens.Operators`, but I had no need to. Lenses were useful in the way I had expected them to be, and so were prisms. One thing led to another, and before long, I understood the relationship between the various optics, the most notable additions to my toolkit being folds and traversals. Sure, the type errors were completely opaque much of the time, but I was able to piece things together with ample type annotations and time spent staring at ill-typed expressions. Before long, I had developed an intuition for `lens`. - -After using it for a while, I retrospected on whether or not I liked it, and honestly, I still can’t decide. Some lensy expressions were straightforward to read and were a pleasant simplification, like this one: - -```haskell -paramSpecs ^.. folded._Required -``` - -Others were less obviously improvements, such as this beauty: - -```haskell -M.fromList $ paramSpecs ^.. folded._Optional.filtered (has $ _2._UsePreviousValue) -``` - -But operator soup aside, there was something deeper about `lens` that bothered me, and I just wasn’t sure what. I didn’t know how to articulate my vague feelings until I read a 2014 blog post entitled [Lens is unidiomatic Haskell][lens-unidiomatic], which includes a point that I think is spot-on: - -> Usually, types in Haskell are rigid. This leads to a distinctive style of composing programs: look at the types and see what fits where. This is impossible with `lens`, which takes overloading to the level mainstream Haskell probably hasn’t seen before. -> -> We have to learn the new language of the `lens` combinators and how to compose them, instead of enjoying our knowledge of how to compose Haskell functions. Formally, `lens` types are Haskell function types, but while with ordinary Haskell functions you immediately see from types whether they can be composed, with `lens` functions this is very hard in practice. -> -> […] -> -> Now let me clarify that this doesn’t necessarily mean that `lens` is a bad library. It’s an *unusual* library. It’s almost a separate language, with its own idioms, embedded in Haskell. - -The way `lens` structures its types deliberately introduces a sort of subtyping relationship—for example, all lenses are traversals and all traversals are folds, but not vice versa—and indeed, knowing this subtyping relationship is essential to working with the library and understanding how to use it. It is helpfully documented with a large diagram on [the `lens` package overview page][lens], and that diagram was most definitely an invaluable resource for me when I was learning how to use the library. - -On the surface, this isn’t unreasonable. Subtyping is an enormously useful concept! The only reason Haskell dispenses with it entirely is because it makes type inference notoriously difficult. The subtyping relation between optics is one of the things that makes them so useful, since it allows you to easily compose a lens with a prism and get a traversal out. Unfortunately, the downside of all this is that Haskell does not truly have subtyping, so all of `lens`’s “types” really must be type aliases for types of roughly the same shape, namely functions. This makes type errors completely *baffling*, since the errors do not mention the aliases, only the fully-expanded types (which are often rather complicated, and their meaning is not especially clear without knowing how `lens` works under the hood). - -So the above quote is correct: working with `lens` really *is* like working in a separate embedded language, but I’m usually okay with that. Embedded, domain-specific languages are good! Unfortunately, in this case, the host language is not very courteous to its guest. Haskell does not appear to be a powerful enough language for `lens` to be a language in its own right, so it must piggyback on top of Haskell’s error reporting mechanisms, which are insufficient for `lens` to be a cohesive linguistic abstraction. Just as debugging code by stepping through the assembly it produces (or, perhaps more relevant in 2018, debugging a compile-to-JS language by looking at the emitted JavaScript instead of the source code) makes for an unacceptably leaky language. We would never stand for such a thing in our general-purpose language tooling, and we should demand better even in our embedded languages. - -That said, `lens` is just too useful to ignore. It is a hopelessly leaky abstraction, but it’s still an abstraction, and a powerful one at that. Given my selection of default extensions as evidence, I think it’s clear I have zero qualms with “advanced” Haskell; I will happily use even `singletons` where it makes sense. Haskell’s various language extensions are sometimes confusing in their own right, but their complexity is usually fundamental to the expressive power they bring. `lens` has some fundamental complexity, too, but it is mostly difficult for the wrong reasons. Still, while it is not the first library I reach for on every new Haskell project, manipulating nested data without `lens` is just too unpleasant after tasting the nectar, so I can’t advise against it in good faith. - -Sadly, this means I’m a bit wishy-washy when it comes to using `lens`, but I do have at least one recommendation: if you decide to use `lens`, it’s better to go all-in. Don’t generate lenses for just a handful of datatypes, do it for *all* of them. You can definitely stick to a subset of the `lens` library’s features, but don’t apply it in some functions but not others. Having too many different, equally valid ways of doing things leads to confusion and inconsistency, and inconsistency minimizes code reuse and leads to duplication and spaghetti. Commit to using `lens`, or don’t use it at all. - -## Mitigating the string problem - -Finally, Haskell has a problem with strings. Namely, `String` is a type alias for `[Char]`, a lazy, singly linked list of characters, which is an awful representation of text. Fortunately, the answer to this problem is simple: ban `String` in your programs. - -Use `Text` everywhere. I don’t really care if you pick strict `Text` or lazy `Text`, but pick one and stick to it. Don’t ever use `String`, and *especially* don’t ever, *ever*, ***ever*** use `ByteString` to represent text! There are enormously few legitimate cases for using `ByteString` in a program that is not explicitly about reading or writing raw data, and even at that level, `ByteString` should only be used at program boundaries. In that sense, I treat `ByteString` much the same way I treat `IO`: push it to the boundaries of your program. - -One of Haskell’s core tenets is making illegal states unrepresentable. Strings are not especially useful datatypes for this, since they are sequences of arbitrary length made up of atoms that can be an enormously large number of different things. Still, string types enforce a very useful invariant, a notion of a sequence of human-readable characters. In the presence of Unicode, this is a more valuable abstraction than it might seem, and the days of treating strings as little different from sequences of bytes are over. While strings make a poor replacement for enums, they are quite effective at representing the incredible amount of text humans produce in a staggeringly large number of languages, and they are the right type for that job. - -`ByteString`, on the other hand, is essentially never the right type for any job. If a type classifies a set of values, `ByteString` is no different from `Any`. It is the structureless type, the all-encompassing blob of bits. A `ByteString` could hold anything at all—some text, an image, an executable program—and the type system certainly isn’t going to help to answer that question. The only use case I can possibly imagine for passing around a `ByteString` in your program rather than decoding it into a more precise type is if it truly holds opaque data, e.g. some sort of token or key provided by a third party with no structure guaranteed whatsoever. Still, even this should be wrapped in a `newtype` so that the type system enforces this opaqueness. - -Troublingly, `ByteString` shows up in many libraries’ APIs where it has no business being. In many cases, this seems to be things where ASCII text is expected, but this is hardly a good reason to willingly accept absolutely anything and everything! Make an `ASCII` type that forbids non-ASCII characters, and provide a `ByteString -> Maybe ASCII` function. Alternatively, think harder about your problem in question to properly support Unicode as you almost certainly ought to. - -Other places `ByteString` appears are similarly unfortunate. Base-64 encoding, for example, could be given the wonderfully illustrative type `ByteString -> Text`, or even `ByteString -> ASCII`! Such a type makes it immediately clear why base-64 is useful: it allows transforming arbitrary binary data into a reliable textual encoding. If we consider that `ByteString` is essentially `Any`, this function has the type `Any -> ASCII`, which is amazingly powerful! We can convert *anything* to ASCII text! - -Existing libraries, however, just provide the boring, disappointingly inaccurate type `ByteString -> ByteString`, which is one of the most useless types there is. It is essentially `Any -> Any`, the meaningless function type. It conveys nothing about what it does, other than that it is pure. Giving a function this type is scarcely better than dynamic typing. Its mere existence is a failure of Haskell library design. - -But wait, it gets worse! `Data.Text.Encoding` exports a function called `decodeUtf8`, which has type `ByteString -> Text`. What an incredible function with a captivating type! Whatever could it possibly do? Again, this function’s type is basically `Any -> Text`, which is remarkable in the power it gives us. Let’s try it out, shall we? - -``` -ghci> decodeUtf8 "\xc3\x28" -"*** Exception: Cannot decode byte '\x28': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream -``` - -Oh. Well, that’s a disappointment. - -Haskell’s string problem goes deeper than `String` versus `Text`; it seems to have wound its way around the collective consciousness of the Haskell community and made it temporarily forget that it cares about types and totality. This isn’t that hard, I swear! I can only express complete befuddlement at how many of these APIs are just completely worthless. - -Fortunately, there is a way out, and that way out is [`text-conversions`][text-conversions]. It is the first Haskell library I ever wrote. It provides *type safe*, *total* conversions between `Text` and various other types, and it is encoding aware. It provides appropriately-typed base-16 and base-64 conversion functions, and is guaranteed to never raise any exceptions. Use it, and apply the Haskell philosophy to your strings, just as you already do for everything else in your program. - -# Closing thoughts - -*Phew.* - -When I started writing this blog post, it used the phrase “short overview” in the introduction. It is now over ten thousand words long. I think that’s all I have it in me to say for now. - -Haskell is a wonderful language built by a remarkable group of people. Its community is often fraught with needlessly inflammatory debates about things like the value of codes of conduct, the evils of Hackage revisions, and precisely how much or how little people ought to care about the monad laws. These flame wars frustrate me to no end, and they sometimes go so far as to make me ashamed to call myself a part of the Haskell community. Many on the “outside” seem to view Haskellers as an elitist, mean-spirited cult, more interested in creating problems for itself than solving them. - -That perception is categorically wrong. - -I have never been in a community of programmers so dedicated and passionate about applying thought and rigor to building software, then going out and *actually doing it*. I don’t know anywhere else where a cutting-edge paper on effect systems is discussed by the very same people who are figuring out how to reliably deploy distributed services to AWS. Some people view the Haskell community as masturbatory, and to some extent, they are probably right. One of my primary motivators for writing Haskell is that it is fun and it challenges me intellectually in ways that other languages don’t. But that challenge is not a sign of uselessness, it is a sign that Haskell is *so close* to letting me do the right thing, to solving the problem the right way, to letting me work without compromises. When I write in most programming languages, I must constantly accept that my program will never be robust in all the ways I want it to be, and I might as well give up before I even start. Haskell’s greatest weakness is that it tempts me to try. - -Haskell is imperfect, as it will always be. I doubt I will ever be satisfied by any language or any ecosystem. There will always be more to learn, more to discover, better tools and abstractions to develop. Many of them will not look anything like Haskell; they may not involve formal verification or static types or effect systems at all. Perhaps live programming, structural editors, and runtime hotswapping will finally take over the world, and we will find that the problems we thought we were solving were irrelevant to begin with. I can’t predict the future, and while I’ve found great value in the Haskell school of program construction, I dearly hope that we do not develop such tunnel vision that we cannot see that there may be other ways to solve these problems. Many of the solutions are things we likely have not even begun to think about. Still, whether that happens or not, it is clear to me that Haskell is a point in the design space unlike any other, and we learn almost as much from the things it gets wrong as we do from the things it gets right. - -It’s been a wonderful two years, Haskell. I won’t be a stranger. - - -[-Wall]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall -[-Wcompat]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat -[-Werror]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Werror -[-Weverything]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Weverything -[-Wincomplete-patterns]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-patterns -[-Wincomplete-record-updates]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates -[-Wincomplete-uni-patterns]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns -[-Wredundant-constraints]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints -[ApplicativeDo]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo -[BangPatterns]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns -[ConstraintKinds]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds -[DataKinds]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds -[DefaultSignatures]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures -[DeriveAnyClass]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass -[DeriveFoldable]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable -[DeriveFunctor]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor -[DeriveGeneric]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric -[DeriveLift]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift -[DeriveTraversable]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable -[DerivingStrategies]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies -[EmptyCase]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase -[EmptyDataDecls]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyDataDecls -[ExistentialQuantification]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification -[FlexibleContexts]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts -[FlexibleInstances]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances -[free-monad-considered-harmful]: https://markkarpov.com/post/free-monad-considered-harmful.html -[freer-simple-documentation]: https://hackage.haskell.org/package/freer-simple-1.0.1.1/docs/Control-Monad-Freer.html -[freer-simple]: https://hackage.haskell.org/package/freer-simple -[FunctionalDependencies]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies -[GADTs]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs -[GeneralizedNewtypeDeriving]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving -[ghc-trac-14471]: https://ghc.haskell.org/trac/ghc/ticket/14471 -[IncoherentInstances]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XIncoherentInstances -[InstanceSigs]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs -[KindSignatures]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures -[LambdaCase]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase -[lens-unidiomatic]: https://ro-che.info/articles/2014-04-24-lens-unidiomatic -[lens]: https://hackage.haskell.org/package/lens -[lifts-for-free]: /blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/ -[MonoLocalBinds]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds -[mtl]: https://hackage.haskell.org/package/mtl -[MultiParamTypeClasses]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses -[MultiWayIf]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf -[NamedFieldPuns]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns -[OverlappingInstances]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverlappingInstances -[OverloadedStrings]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings -[PatternSynonyms]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms -[PolyKinds]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPolyKinds -[QuasiQuotes]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XQuasiQuotes -[RankNTypes]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes -[RecordWildCards-accidental-identifier-capture]: https://www.reddit.com/r/haskell/comments/6jaa5f/recordwildcards_and_binary_parsing/djd5ugj/ -[RecordWildCards]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards -[ScopedTypeVariables]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables -[StandaloneDeriving]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving -[TemplateHaskell]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell -[text-conversions]: https://hackage.haskell.org/package/text-conversions -[TupleSections]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections -[TypeApplications]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications -[TypeFamilies]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies -[TypeFamilyDependencies]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies -[TypeOperators]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators -[UndecidableInstances]: https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XUndecidableInstances diff --git a/blog/posts/2018-04-15-reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket.md b/blog/posts/2018-04-15-reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket.md deleted file mode 100644 index d1abb68..0000000 --- a/blog/posts/2018-04-15-reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket.md +++ /dev/null @@ -1,616 +0,0 @@ - Title: Reimplementing Hackett’s type language: expanding to custom core forms in Racket - Date: 2018-04-15T00:00:00 - Tags: racket, hackett, macros - -In the past couple of weeks, I [completely rewrote the implementation of Hackett’s type language][hackett-type-language-commit] to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret. - -This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like [my previous blog post on Hackett][hackett-namespaces-blog-post], which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change. - -# What are core forms? - -Before we can get started writing *custom core forms*, we need to understand the meaning of Racket’s plain old *core forms*. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works. - -To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a `#lang` line at the top. In this case, we’ll use `#lang racket` to keep things simple: - -```racket -#lang racket - -(define (add2 x) - (+ x 2)) - -(add2 3) -``` - -How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as *reading* the program. The `#lang` line controls how the program is read—some `#lang`s provide parsers that allow syntax that is very different from the parser used for `#lang racket`—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this: - -```racket -(module m racket - (#%module-begin - (define (add2 x) - (+ x 2)) - - (add2 3))) -``` - -Note the introduction of `#%module-begin`. Despite the fancy name, this is really just an ordinary macro provided by the `racket` language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with `#%module-begin`; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module. - -One the program has been read, it is subsequently *expanded* by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different: - -```racket -(module m racket - (#%plain-module-begin - (define-values (add2) - (lambda (x) (#%plain-app + x '2))) - - (#%plain-app call-with-values - (lambda () (#%plain-app add2 '3)) - print-values))) -``` - -Let’s note the things that changed: - - 1. `#%module-begin` was replaced with `#%plain-module-begin`. `#%plain-module-begin` is a binding that wraps the body of every expanded module, and all definitions of `#%module-begin` in any language must eventually expand to `#%plain-module-begin`. However, `#lang racket`’s `#%module-begin` doesn’t *just* expand to `#%plain-module-begin`, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints `5` even though there is no code related to printing in the original program! - - 2. The lambda shorthand used with `define` was converted to an explicit use of `lambda`, and it was expanded to `define-values`. In Racket, `define` and `define-syntax` are really just macros for `define-values` and `define-syntaxes` that only bind a single identifier. - - 3. All function applications were tagged explicitly with `#%plain-app`. This syntactically distinguishes function applications from uses of forms like `define-values` or `lambda`. It also allows languages to customize function application by providing their own macros named `#%app` (just like languages can provide their own macros named `#%module-begin` that expand to `#%plain-module-begin`), but that is outside the scope of this blog post. - - 4. All literals have been wrapped with `quote`, so `2` became `'2` and `3` became `'3`. - -Importantly, the resulting program contains **no macros**. Such programs are called *fully expanded*, since all macros have been eliminated and no further expansion can take place. - -So what’s left behind? Well, some of the things in the program are literal data, like the numbers `2` and `3`. There are also some variable references, `x` and `add2`. Most of the program, however, is built out of primitives like `module`, `#%plain-module-begin`, `#%plain-app`, `define-values`, and `lambda`. These primitives are *core forms*—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further. - -In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program: - -```python -var add2 = - function (x) { return x + 2; }; - -console.log(add2(3)); -``` - -Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be *compiled*. The Racket compiler has built-in rules for how to compile core forms like `define-values`, `lambda`, and `#%plain-app`, and the result is optimized Racket bytecode. - -In the remainder of this blog post, as most discussions of macros do, we’ll ignore the *read* and *compile* steps of the Racket program pipeline and focus exclusively on the *expand* step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them. - -## Racket’s default core forms - -So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered `module`, `#%plain-module-begin`, `#%plain-app`, `define-values`, `lambda`, and `quote`, but there are many more. The full list is available in the section of the Racket reference named [Fully Expanded Programs][reference-fully-expanded-programs], and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like `define-syntaxes`, `if`, `let-values`, `letrec-values`, `begin`, `quote-syntax`, and `set!`. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion. - -These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, `#lang racket` implements `cond` as a macro that eventually expands into `if`, and it implements `syntax` as a macro that eventually expands into function calls and `quote-syntax`. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s `match` can expand into uses of `let` and `cond`, and it doesn’t need to concern itself with using `let-values` and `if`. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched. - -## The need for custom core forms - -With that in mind, why might we wish to define *custom* core forms? In fact, what would such a thing even mean? By their very nature, *all* Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them. - -Despite this, there *are* at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea. - -### Supporting multiple backends - -The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket `#lang` that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might *also* wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets. - -In the case of a hardware description language, the custom core forms might include things like `input` and `output` for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could *additionally* define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step. - -Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem. - -### Compiling an extensible embedded language - -A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume. - -This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it *will* eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis. - -Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the `match` macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms. - -Existing DSLs of this type are rare, but they do exist. `syntax/parse` provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from `racket/match` to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While `match` is essentially just a traditional pattern-matcher, albeit an extensible one, `syntax-parse` is its own programming language, closer in some ways to Prolog than to Racket. - -For this reason, `syntax/parse` has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as [*pattern directives*][syntax-parse-pattern-directives]. Here is an example of pattern directives in action, from my own `threading` library: - -```racket -[(_ ex:expr cl:clause remaining:clause ...) - #:do [(define call (syntax->list #'cl.call)) - (define-values (pre post) - (split-at call (add1 (or (attribute cl.insertion-point) 0))))] - #:with [pre ...] pre - #:with [post ...] post - #:with app/ctx (adjust-outer-context this-syntax #'(pre ... ex post ...) #'cl) - (adjust-outer-context this-syntax #'(~> app/ctx remaining ...) this-syntax)] -``` - -Each directive is represented by a keyword, in this case `#:do` and `#:with`. Each directive has a corresponding keyword in the pattern language, in this case `~do` and `~parse`. Therefore, the above pattern could equivalently be written this way: - -```racket -[{~and (_ ex:expr cl:clause remaining:clause ...) - {~do (define call (syntax->list #'cl.call)) - (define-values (pre post) - (split-at call (add1 (or (attribute cl.insertion-point) 0))))} - {~parse [pre ...] pre} - {~parse [post ...] post} - {~parse app/ctx (adjust-outer-context this-syntax #'(pre ... ex post ...) #'cl)}} - (adjust-outer-context this-syntax #'(~> app/ctx remaining ...) this-syntax)] -``` - -The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using `#:declare`, so this is also equivalent: - -```racket -[(_ ex cl remaining ...) - #:declare ex expr - #:declare cl clause - #:declare remaining clause - #:do [(define call (syntax->list #'cl.call)) - (define-values (pre post) - (split-at call (add1 (or (attribute cl.insertion-point) 0))))] - #:with [pre ...] pre - #:with [post ...] post - #:with app/ctx (adjust-outer-context this-syntax #'(pre ... ex post ...) #'cl) - (adjust-outer-context this-syntax #'(~> app/ctx remaining ...) this-syntax)] -``` - -This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a `#:with` or `~parse` pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using `#:do` or `~do`, practical uses of `syntax/parse` really do involve quite a lot of programming in its pattern DSL. - -But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—`#:declare`, `#:do`, and `#:with`, among others—are essentially the core forms of `syntax/parse`’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros. - -But what if `syntax/parse` could define its own core forms? What if, instead of `#:do`, `#:declare`, and `#:with` being implemented as keyword options specially recognized by the `syntax-parse` grammar, it defined `do`, `declare`, and `with` as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the `syntax/parse` core forms. The implementation of `syntax/parse` could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code. - -Now, to be fair, `syntax/parse` is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms. - -The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis. - -# The need for a custom type language in Hackett - -In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for *both* of them: - - - Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime. - - - Hackett can *also* make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language. - -The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms: - - - (#%type:con *id*) — Type constructors, like `Integer` or `Maybe`. These are one of the fundamental building blocks of Hackett types. - - - (#%type:app *type* *type*) — Type application, such as `(Maybe Integer)`. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of `#%type:app`. - - - (#%type:forall *id* *type*) — Universal quantification. This is essentially a binding form, which binds any uses of (#%type:bound-var *id*) in *type*. - - - (#%type:qual *type* *type*) — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like `Eq` are bound as type constructors. - - - Finally, Hackett types support three different varieties of type variables: - - - (#%type:bound-var *id*) — Bound type variables. These are only legal under a corresponding `#%type:forall`. - - - (#%type:wobbly-var *id*) — Solver variables, which may unify with any other type as part of the typechecking process. - - - (#%type:rigid-var *id*) — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic. - -To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we *can* bind them to macros that do nothing but raise an exception if something attempts to expand them: - -```racket -(define-syntaxes [#%type:con #%type:app #%type:forall #%type:qual - #%type:bound-var #%type:wobbly-var #%type:rigid-var] - (let ([type-literal (λ (stx) (raise-syntax-error #f "cannot be used as an expression" stx))]) - (values type-literal type-literal type-literal type-literal - type-literal type-literal type-literal))) -``` - -This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism. - -## Expanding types in our type language - -We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we *do* want to expand some of their subforms. For example, in the type `(#%type:app a b)`, we want to recursively expand `a` and `b`. - -In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called [`local-expand`][local-expand], and it has an option relevant to our needs: the stop list. - -Often, `local-expand` is used to force the expander to completely, recursively expand a form. For example, by using `local-expand`, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros: - -```racket -(local-expand #'(let ([x 1]) (+ x 2)) 'expression '()) -; => (let-values ([(x) '1]) (#%plain-app + x '2)) -``` - -The third argument to `local-expand` is the *stop list*, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list: - -```racket -(begin-for-syntax - (define type-literal-ids - (list #'#%type:con #'#%type:app #'#%type:forall #'#%type:qual - #'#%type:bound-var #'#%type:wobbly-var #'#%type:rigid-var)) - - (local-expand #'(#%type:forall x t) 'expression type-literal-ids)) - ; => (#%type:forall x t) -``` - -Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the `#%type:forall` identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding `t` since the expander has no way of knowing which pieces of `(#%type:forall x t)` it should expand! In this case, we want it to recur to expand `t`, since it should be a type, but not `x`, since `#%type:forall` essentially puts `x` in binding position. - -Therefore, we have to get more clever. We need to call `local-expand` to produce a type, then we have to pattern-match on it and subsequently call `local-expand` *again* on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded. - -One good way to do this is to use `syntax/parse` syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using `local-expand` and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled `#%type:con` and `#%type:app`: - -```racket -(begin-for-syntax - (define-literal-set type-literals - [#%type:con #%type:app #%type:forall #%type:qual - #%type:bound-var #%type:wobbly-var #%type:rigid-var]) - - (define-syntax-class type - #:description "type" - #:attributes [expansion] - [pattern _ #:with :expanded-type - (local-expand this-syntax 'expression type-literal-ids)]) - - (define-syntax-class expanded-type - #:description #f - #:attributes [expansion] - #:commit - #:literal-sets [type-literals] - [pattern (#%type:con ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:app ~! a:type b:type) - #:attr expansion #'(#%type:app a.expansion b.expansion)])) -``` - -This blog post is definitely *not* a `syntax/parse` tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named `expansion`. This attribute contains the fully expanded version of the type currently being parsed. In the `#%type:con` case, `expansion` is just `this-syntax`, which holds the current piece of syntax being parsed. This makes sense, since uses of `#%type:con` just expand to themselves—expanding `(#%type:con Maybe)` should not perform any additional expansion on `Maybe`. This is one of Hackett’s atomic types. - -In contrast, `#%type:app` *does* recursively expand its arguments. By annotating its two subforms with `:type`, the `type` syntax class will invoke `local-expand` on each subform, which will in turn use `expanded-type` to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once `a` and `b` have been expanded, `#%type:app` reassembles them into a new syntax object using `#'(#%type:app a.expansion b.expansion)`, which replaces their unexpanded versions with their new, expanded versions. - -We can see this behavior by writing a small `expand-type` function that will expand its argument: - -```racket -(begin-for-syntax - (define expand-type (syntax-parser [t:type #'t.expansion]))) -``` - -Now we can use it to observe what happens when we try expanding a type using `#%type:app`: - -```racket -(expand-type #'(#%type:app Maybe Integer)) -; => #%type:app: expected type -; at: Maybe -; in: (#%type:app Maybe Integer) -``` - -Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined `Maybe` or `Integer` anywhere. Let’s do so! We can define them as simple macros that expand into uses of `#%type:con`, which can be done easily using [`make-variable-like-transformer`][make-variable-like-transformer] from `syntax/transformer`: - -```racket -(define-syntax Maybe (make-variable-like-transformer #'(#%type:con Maybe))) -(define-syntax Integer (make-variable-like-transformer #'(#%type:con Integer))) -``` - -Now, if we try expanding that same type again: - -```racket -(expand-type #'(#%type:app Maybe Integer)) -; => (#%type:app (#%type:con Maybe) (#%type:con Integer)) -``` - -…it works! Neat. Now we just need to add the cases for the remaining forms in our type language: - -```racket -(begin-for-syntax - (define-syntax-class expanded-type - #:description #f - #:attributes [expansion] - #:commit - #:literal-sets [type-literals] - [pattern (#%type:con ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:app ~! a:type b:type) - #:attr expansion #'(#%type:app a.expansion b.expansion)] - [pattern (#%type:forall ~! x:id t:type) - #:attr expansion #'(#%type:forall x t.expansion)] - [pattern (#%type:qual ~! a:type b:type) - #:attr expansion #'(#%type:qual a.expansion b.expansion)] - [pattern (#%type:bound-var ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:wobbly-var ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:rigid-var ~! _:id) - #:attr expansion this-syntax])) -``` - -This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like `let-syntax` to work in types. For example, we ought to be able to create a local type binding with `let-syntax` and have it just work. Unfortunately, it doesn’t: - -```racket -(expand-type #'(let-syntax ([Bool (make-variable-like-transformer #'(#%type:con Bool))]) - (#%type:app Maybe Bool))) -; => let-syntax: expected one of these identifiers: `#%type:con', `#%type:app', `#%type:forall', `#%type:qual', `#%type:bound-var', `#%type:wobbly-var', or `#%type:rigid-var' -; at: letrec-syntaxes+values -; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool)) -``` - -What went wrong? And why is it complaining about `letrec-syntaxes+values`? Well, if you read the documentation for `local-expand`, you’ll find that its behavior is a little more complicated than you might at first believe: - -> If *`stop-ids`* is [a nonempty list containing more than just `module*`], then `begin`, `quote`, `set!`, `#%plain-lambda`, `case-lambda`, `let-values`, `letrec-values`, `if`, `begin0`, `with-continuation-mark`, `letrec-syntaxes+values`, `#%plain-app`, `#%expression`, `#%top`, and `#%variable-reference` are implicitly added to *`stop-ids`*. Expansion stops when the expander encounters any of the forms in *`stop-ids`*, and the result is the partially-expanded form. - -That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with `letrec-syntaxes+values` (which `let-syntax` expands to) is a reasonable one. If the expander naïvely expanded `letrec-syntaxes+values` in the presence of a nonempty stop list, it could cause some significant problems! - -Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program: - -```racket -(let-syntax ([Bool (make-variable-like-transformer #'(#%type:con Bool))]) - (#%type:app Maybe Bool)) -``` - -We see `let-syntax`, so we start by evaluating the expression on the right hand side of the `Bool` binding. This produces a transformer expression, so we bind `Bool` to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this: - -```racket -; local bindings: -; Bool -> # -(#%type:app Maybe Bool) -``` - -Now, the identifier in application position is `#%type:app`, and `#%type:app` is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the `let-syntax` needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is `(#%type:app Maybe Bool)`. But this is a problem, because when we then go to expand `Bool`, `Bool` isn’t in the local binding table anymore! The `let-syntax` was already erased, and `Bool` is unbound! - -When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little. - -Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call `local-expand`. The bad news is that first-class definition contexts are *complicated*, and using them properly is a surprisingly subtle problem. - -Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our `type` and `expanded-type` syntax classes so that we may thread a definition context around as we recursively expand: - -```racket -(begin-for-syntax - (define-syntax-class (type [intdef-ctx #f]) - #:description "type" - #:attributes [expansion] - [pattern _ #:with {~var || (expanded-type intdef-ctx)} - (local-expand this-syntax 'expression type-literal-ids intdef-ctx)]) - - (define-syntax-class (expanded-type intdef-ctx) - #:description #f - #:attributes [expansion] - #:commit - #:literal-sets [type-literals] - [pattern (#%type:con ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)}) - #:attr expansion #'(#%type:app a.expansion b.expansion)] - [pattern (#%type:forall ~! x:id {~var t (type intdef-ctx)}) - #:attr expansion #'(#%type:forall x t.expansion)] - [pattern (#%type:qual ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)}) - #:attr expansion #'(#%type:qual a.expansion b.expansion)] - [pattern (#%type:bound-var ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:wobbly-var ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:rigid-var ~! _:id) - #:attr expansion this-syntax])) -``` - -Now, we can add an additional case to `expanded-type` to handle `letrec-syntaxes+values`, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body: - -```racket -[pattern (letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr) - #:do [(define intdef-ctx* (syntax-local-make-definition-context)) - (for ([ids (in-list (attribute id))] - [e (in-list (attribute e))]) - (syntax-local-bind-syntaxes ids e intdef-ctx*))] - #:with {~var t* (type intdef-ctx*)} #'t - #:attr expansion #'t*.expansion] -``` - -But even this isn’t quite right. The problem with this implementation is that it throws away the existing `intdef-ctx` argument to `expanded-type`, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a *child* of the previous definition context by passing the old context as an argument to `syntax-local-make-definition-context`. This will ensure the parent bindings are brought into scope when expanding using the child context: - -```racket -[pattern (letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr) - #:do [(define intdef-ctx* (syntax-local-make-definition-context intdef-ctx)) - (for ([ids (in-list (attribute id))] - [e (in-list (attribute e))]) - (syntax-local-bind-syntaxes ids e intdef-ctx*))] - #:with {~var t* (type intdef-ctx*)} #'t - #:attr expansion #'t*.expansion] -``` - -With this in place, our example using `let-syntax` actually works! - -```racket -(expand-type #'(let-syntax ([Bool (make-variable-like-transformer #'(#%type:con Bool))]) - (#%type:app Maybe Bool))) -; => (#%type:app (#%type:con Maybe) (#%type:con Bool)) -``` - -Pretty cool, isn’t it? - -## Preserving syntax properties and source locations - -We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful. - -To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports `#%type:app` under a different name, like `#%type:apply`, we should expand to a piece of syntax that still has `#%type:apply` in application position instead of replacing it with `#%type:app`. - -To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the `#%type:app` clause to the following: - -```racket -[pattern (head:#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)}) - #:attr expansion #'(head a.expansion b.expansion)] -``` - -But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy *everything*, we can define two helper macros, `syntax/loc/props` and `quasisyntax/loc/props`, which are like `syntax/loc` and `quasisyntax/loc` but copy properties in addition to source location information: - -```racket -(begin-for-syntax - (define-syntaxes [syntax/loc/props quasisyntax/loc/props] - (let () - (define (make-syntax/loc/props name syntax-id) - (syntax-parser - [(_ from-stx-expr:expr {~describe "template" template}) - #`(let ([from-stx from-stx-expr]) - (unless (syntax? from-stx) - (raise-argument-error '#,name "syntax?" from-stx)) - (let* ([stx (#,syntax-id template)] - [stx* (syntax-disarm stx #f)]) - (syntax-rearm (datum->syntax stx* (syntax-e stx*) from-stx from-stx) stx)))])) - (values (make-syntax/loc/props 'syntax/loc/props #'syntax) - (make-syntax/loc/props 'quasisyntax/loc/props #'quasisyntax))))) -``` - -Using `syntax/loc/props`, we can be truly thorough about ensuring all properties are preserved: - -```racket -[pattern (head:#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)}) - #:attr expansion (syntax/loc/props this-syntax - (head a.expansion b.expansion))] -``` - -Applying this to the other relevant clauses, we get an updated version of the `expanded-type` syntax class: - -```racket -(begin-for-syntax - (define-syntax-class (expanded-type intdef-ctx) - #:description #f - #:attributes [expansion] - #:commit - #:literal-sets [kernel-literals type-literals] - [pattern (letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr) - #:do [(define intdef-ctx* (syntax-local-make-definition-context intdef-ctx)) - (for ([ids (in-list (attribute id))] - [e (in-list (attribute e))]) - (syntax-local-bind-syntaxes ids e intdef-ctx*))] - #:with {~var t* (type intdef-ctx*)} #'t - #:attr expansion #'t*.expansion] - [pattern (#%type:con ~! _:id) - #:attr expansion this-syntax] - [pattern (head:#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)}) - #:attr expansion (syntax/loc/props this-syntax - (head a.expansion b.expansion))] - [pattern (head:#%type:forall ~! x:id {~var t (type intdef-ctx)}) - #:attr expansion (syntax/loc/props this-syntax - (head x t.expansion))] - [pattern (head:#%type:qual ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)}) - #:attr expansion (syntax/loc/props this-syntax - (head a.expansion b.expansion))] - [pattern (#%type:bound-var ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:wobbly-var ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:rigid-var ~! _:id) - #:attr expansion this-syntax])) -``` - -Now we’re getting closer, but if you can believe it, even *this* isn’t good enough. The real expander’s implementation of `letrec-syntaxes+values` does two things our implementation does not: it copies properties and updates the `'origin` property to indicate the syntax came from a use of `letrec-syntaxes+values`, and it adds a `'disappeared-use` property to record the erased bindings for use by tools like DrRacket. We can apply `syntax-track-origin` and `internal-definition-context-track` to the resulting syntax to add the same properties the expander would: - -```racket -[pattern (head:letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr) - #:do [(define intdef-ctx* (syntax-local-make-definition-context intdef-ctx)) - (for ([ids (in-list (attribute id))] - [e (in-list (attribute e))]) - (syntax-local-bind-syntaxes ids e intdef-ctx*))] - #:with {~var t* (type intdef-ctx*)} #'t - #:attr expansion (~> (internal-definition-context-track intdef-ctx* #'t*.expansion) - (syntax-track-origin this-syntax #'head))] -``` - -Now we’ve *finally* dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit. - -## Connecting our custom language to Hackett - -It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code: - -```racket -#lang racket/base - -(require (for-meta 2 racket/base - syntax/parse) - (for-syntax racket/base - syntax/intdef - threading) - syntax/parse/define) - -(begin-for-syntax - (define-syntaxes [syntax/loc/props quasisyntax/loc/props] - (let () - (define (make-syntax/loc/props name syntax-id) - (syntax-parser - [(_ from-stx-expr:expr {~describe "template" template}) - #`(let ([from-stx from-stx-expr]) - (unless (syntax? from-stx) - (raise-argument-error '#,name "syntax?" from-stx)) - (let* ([stx (#,syntax-id template)] - [stx* (syntax-disarm stx #f)]) - (syntax-rearm (datum->syntax stx* (syntax-e stx*) from-stx from-stx) stx)))])) - (values (make-syntax/loc/props 'syntax/loc/props #'syntax) - (make-syntax/loc/props 'quasisyntax/loc/props #'quasisyntax))))) - -(define-syntaxes [#%type:con #%type:app #%type:forall #%type:qual - #%type:bound-var #%type:wobbly-var #%type:rigid-var] - (let ([type-literal (λ (stx) (raise-syntax-error #f "cannot be used as an expression" stx))]) - (values type-literal type-literal type-literal type-literal - type-literal type-literal type-literal))) - -(begin-for-syntax - (define type-literal-ids - (list #'#%type:con #'#%type:app #'#%type:forall #'#%type:qual - #'#%type:bound-var #'#%type:wobbly-var #'#%type:rigid-var)) - - (define-literal-set type-literals - [#%type:con #%type:app #%type:forall #%type:qual - #%type:bound-var #%type:wobbly-var #%type:rigid-var]) - - (define-syntax-class (type [intdef-ctx #f]) - #:description "type" - #:attributes [expansion] - [pattern _ #:with {~var || (expanded-type intdef-ctx)} - (local-expand this-syntax 'expression type-literal-ids intdef-ctx)]) - - (define-syntax-class (expanded-type intdef-ctx) - #:description #f - #:attributes [expansion] - #:commit - #:literal-sets [kernel-literals type-literals] - [pattern (head:letrec-syntaxes+values ~! ([(id:id ...) e:expr] ...) () t:expr) - #:do [(define intdef-ctx* (syntax-local-make-definition-context intdef-ctx)) - (for ([ids (in-list (attribute id))] - [e (in-list (attribute e))]) - (syntax-local-bind-syntaxes ids e intdef-ctx*))] - #:with {~var t* (type intdef-ctx*)} #'t - #:attr expansion (~> (internal-definition-context-track intdef-ctx* #'t*.expansion) - (syntax-track-origin this-syntax #'head))] - [pattern (#%type:con ~! _:id) - #:attr expansion this-syntax] - [pattern (head:#%type:app ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)}) - #:attr expansion (syntax/loc/props this-syntax - (head a.expansion b.expansion))] - [pattern (head:#%type:forall ~! x:id {~var t (type intdef-ctx)}) - #:attr expansion (syntax/loc/props this-syntax - (head x t.expansion))] - [pattern (head:#%type:qual ~! {~var a (type intdef-ctx)} {~var b (type intdef-ctx)}) - #:attr expansion (syntax/loc/props this-syntax - (head a.expansion b.expansion))] - [pattern (#%type:bound-var ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:wobbly-var ~! _:id) - #:attr expansion this-syntax] - [pattern (#%type:rigid-var ~! _:id) - #:attr expansion this-syntax]) - - (define expand-type (syntax-parser [t:type #'t.expansion]))) -``` - -But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a *typechecker*. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense. - -But we don’t *just* need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom `#%app` binding that converts n-ary type applications to nested uses of `#%type:app`, as well as a nicer `forall` macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of `#%app`—one for types and one for values—since [Hackett supports multiple namespaces][hackett-namespaces-blog-post], and each can have its own `#%app` binding. - -The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom `syntax/parse` pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, [it’s available on GitHub here][hackett-type-language-source]. - -# Evaluation, limitations, and acknowledgements - -Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started [my new job][tweet-new-job] this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net *removal* of 100 lines from the rest of the codebase, which I consider an organizational win. - -As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I [able to get it working][tweet-typeclass-deriving-1] quickly and easily, I was able to do it in [no more than 20 lines of code][tweet-typeclass-deriving-2]. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction. - -Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the `local-expand` stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes `syntax-parameterize`, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like `syntax-parameterize` cooperate better with things like Hackett’s type language. - -Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to [Matthew Flatt][mflatt], without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank [Ryan Culpepper][ryanc] for [his incredible work on constructing tools for the working macro developer][ryanc-dissertation], including writing the fantastic `syntax/parse` library that powers essentially everything I do. Thank you both. - - - -[hackett-namespaces-blog-post]: /blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/ -[hackett-type-language-commit]: https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935 -[hackett-type-language-source]: https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt -[local-expand]: http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29 -[make-variable-like-transformer]: http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29 -[mflatt]: http://www.cs.utah.edu/~mflatt/ -[reference-fully-expanded-programs]: http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29 -[ryanc]: http://www.ccs.neu.edu/home/ryanc/ -[ryanc-dissertation]: https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf -[syntax-parse-pattern-directives]: http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29 -[tweet-new-job]: https://twitter.com/lexi_lambda/status/976533916596097024 -[tweet-typeclass-deriving-1]: https://twitter.com/lexi_lambda/status/985051504867446786 -[tweet-typeclass-deriving-2]: https://twitter.com/lexi_lambda/status/985052476473856000 diff --git a/blog/posts/2018-09-13-custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions.md b/blog/posts/2018-09-13-custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions.md deleted file mode 100644 index 4d425eb..0000000 --- a/blog/posts/2018-09-13-custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions.md +++ /dev/null @@ -1,686 +0,0 @@ - Title: Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions - Date: 2018-09-13T00:00:00 - Tags: racket, macros - -In my [previous blog post][custom-core-forms-1], I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system. - -# What are internal definitions? - -This blog post is going to be largely focused on how to properly implement a form that handles the expansion of *internal definitions* in Racket. This is a tricky topic to get right, but before we can discuss internal definitions, we have to establish what definitions themselves are and how they relate to other binding forms. - -In a traditional Lisp, there are two kinds of bindings: top-level bindings and local bindings. In Scheme and its descendants, this distinction is characterized by two different binding forms, `define` and `let`. To a first approximation, `define` is used for defining top-level, global bindings, and it resembles variable definitions in many mainstream languages in the sense that definitions using `define` are not really expressions. They don’t produce a value, they define a new binding. Definitions written with `define` look like this: - -```racket -(define x 42) -(define y "hello") -``` - -Each definition is made up of two parts: the *binding identifier*, in this case `x` and `y`, and the *right hand side*, or RHS for short. Each RHS is a single expression that will be evaluated and used as the value for the introduced binding. - -In Scheme and Racket, `define` also supports a shorthand form for defining functions in a natural syntax without the explicit need to write `lambda`, which looks like this: - -```racket -(define (double x) - (* x 2)) -``` - -However, this is just syntactic sugar. The above form is really just a macro for the following equivalent, expanded version: - -```racket -(define double - (lambda (x) (* x 2))) -``` - -Since we only care about fully-expanded programs, we’ll focus exclusively on the expanded version of `define` in this blog post, since if we handle that, we’ll also handle the function shorthand’s expansion. - -In contrast to `define`, there is also `let`, which has a rather different shape. A `let` form *is* an expression, and it creates local bindings in a delimited scope: - -```racket -(let ([x 2] - [y 3]) - (+ x y)) -``` - -The binding clauses of a `let` expression are known as the *binding pairs*, and the sequence of expressions afterwards are known as the *body* of the `let`. Each binding pair consists of a binding identifier and a RHS, just like a top-level definition created with `define`, but while `define` is a standalone form, the binding pairs cannot meaningfully exist outside of a `let`—they are recognized as part of the grammar of the `let` form itself. - -Like other Lisps, Racket distinguishes between top-level—or, more precisely, *module-level*—bindings and local bindings. A module-level binding can be exported using `provide`, which will allow other modules to access the binding by importing the module with `require`. Such definitions are treated specially by the macroexpander, compiler, and runtime system alike. There is a pervasive, meaningful difference between module-level definitions and local definitions besides simply scope. - -I am making an effort to make this as clear as possible before discussing internal definitions because without it, the following point can be rather confusing: internal definitions are written using `define`, but they are local bindings, *not* module-level ones! In Racket, `define` is allowed to appear in the body of virtually all block forms like `let`, so the following is a legal program: - -```racket -(let () - (define x 2) - (define y 3) - (+ x y)) -``` - -This program is equivalent to the one expressed using `let`. In fact, when the Racket macroexpander expands these local uses of `define`, it actually translates them into uses of `letrec`. After expanding the above expression, it would look closer to the following: - -```racket -(let () - (letrec ([x 2] - [y 3]) - (+ x y))) -``` - -In this sense, `define` is a form with a double life in Racket. When used at the module level, it creates module-level definitions, which remain in a fully-expanded program and can be imported by other modules. When used inside local blocks, it creates internal definitions, which do not remain in fully expanded programs, since they are translated into recursive local binding forms. - -In this blog post, we will ignore module-level definitions. Like in the previous blog post, we will focus exclusively on expanding expressions, not whole modules. However, we will extend our language to allow internal definitions inside local binding forms, and we will translate them into `letrec` forms in the same way as the Racket macroexpander. - -# Revisiting and generalizing the expression expander - -In the previous blog post, our expander expanded types, which were essentially expressions from the perspective of the Racket macroexpander. We wrote a syntax class that handled the parsing of a restricted type grammar that disallowed most Racket-level expression forms, like `begin`, `if`, `#%plain-lambda`, and `quote`. After all, Hackett is not dependently-typed, and it disallows explicit type abstraction to preserve type inference, so it would be a very bad thing if we allowed `if` or explicit lambda abstraction to appear in our types. For this blog post, however, we will restructure the type expander to handle the full grammar of expressions permitted by Racket. - -While the syntax class approach used in the previous blog post was cute, this blog post will use ordinary functions defined at phase 1 instead of syntax classes. In practice, this provides superior error reporting, since it reports syntax errors in terms of the form that went wrong, not the form prior to expansion. Since we can still use `syntax-parse` to parse the arguments to these functions, we don’t lose any expressive power in the expression of our pattern language. - -To start, we’ll extract the call to `local-expand` into its own function. This corresponds to the `type` syntax class from the previous blog post, but we’ll use phase 1 parameters to avoid threading so many explicit function arguments around: - -```racket -(begin-for-syntax - (define current-context (make-parameter #f)) - (define current-stop-list (make-parameter (list #'define-values #'define-syntaxes))) - (define current-intdef-ctx (make-parameter #f)) - - (define (current-expand stx) - (local-expand stx - (current-context) - (current-stop-list) - (current-intdef-ctx)))) -``` - -Due to the way `local-expand` implicitly extends the stop list, as discussed in the previous blog post, we can initialize the stop list to a list containing just `define-values` and `define-syntaxes`, and the other forms we care about will be included automatically. - -Next, we’ll use this function to implement a `expand-expression` function, which will emulate the way the expander expands a single expression, as the name implies. We’ll ignore any custom core forms for now, so we’ll just focus exclusively on the Racket core forms. - -A few of Racket’s core forms are not actually subject to any expansion at all, and they expand to themselves. These forms are `quote`, `quote-syntax`, and `#%variable-reference`. Additionally, `#%top` is not something useful to handle ourselves, since it involves no recursive expansion, so we’ll treat it as if it expands to itself as well and allow the expander to raise any unbound identifier errors it produces. Here’s what the `expand-expression` function looks like when exclusively handling these things: - -```racket -(define (expand-expression stx) - (syntax-parse (parameterize ([current-context 'expression]) - (current-expand stx)) - #:literal-sets [kernel-literals] - [({~or quote quote-syntax #%top #%variable-reference} ~! . _) - this-syntax])) -``` - -Another set of Racket core forms are simple expressions which contain subforms, all of which are themselves expressions. These forms include things like `#%expression`, `begin`, and `if`, and they can be expanded recursively. We’ll add another clause to handle these, which can be written with a straightforward recursive call to `expand-expression`: - -```racket -[({~and head {~or #%expression #%plain-app begin begin0 if with-continuation-mark}} ~! form ...) - #:with [form* ...] (map expand-expression (attribute form)) - (syntax/loc/props this-syntax - (head form* ...))] -``` - -Another easy form to handle is `set!`, since it also requires simple recursive expansion, but it can’t be handled in the same way as the above forms since one of its subforms (the variable to mutate) should not be expanded. It needs another small clause: - -```racket -[(head:set! ~! x:id rhs) - (quasisyntax/loc/props this-syntax - (head x #,(expand-expression #'rhs)))] -``` - -The other expressions are harder, since they’re all the binding forms. Fully-expanded Racket code has four local binding forms: `#%plain-lambda`, `case-lambda`, `let-values`, and `letrec-values`. Additionally, as discussed in the previous blog post, `local-expand` can also produce `letrec-syntaxes+values` forms produced by local syntax bindings. In the type expander, we completely disallowed runtime bindings from appearing in the resulting program, so we completely removed `letrec-syntaxes+values` in our expansion, but in the case of handling arbitrary Racket programs, we actually want to leave a `letrec-values` form behind to hold any runtime bindings (i.e. the `values` part of `letrec-syntaxes+values`). - -We’ll start with `#%plain-lambda`, which is the simplest of all the five aforementioned binding forms. It binds a sequence of identifiers at runtime, and they are in scope within the body of the lambda expression. Just as we created and used an internal-definition context to hold the bindings of a `letrec-syntax+values` form in the previous blog post, we’ll do the same for Racket’s other binding forms as well: - -```racket -[(head:#%plain-lambda ~! [x:id ...] body ...) - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (attribute x) #f intdef-ctx)] - #:with [x* ...] (internal-definition-context-introduce intdef-ctx #'[x ...]) - #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute body))) - (syntax/loc/props this-syntax - (head [x* ...] body* ...))] -``` - -However, the above handling of `#%plain-lambda` isn’t *quite* right, since the argument list can also include a “rest argument” binding in addition to a sequence of positional arguments. To accommodate this, we can introduce a simple syntax class that handles the different permutations: - -```racket -(begin-for-syntax - (define-syntax-class plain-formals - #:description "formals" - #:attributes [[id 1]] - #:commit - [pattern (id:id ...)] - [pattern (id*:id ... . id**:id) #:with [id ...] #'[id* ... id**]])) -``` - -Now we can use this to adjust `#%plain-lambda` to handle rest arguments: - -```racket -[(head:#%plain-lambda ~! formals:plain-formals body ...) - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (attribute formals.id) #f intdef-ctx)] - #:with formals* (internal-definition-context-introduce intdef-ctx #'formals) - #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute body))) - (syntax/loc/props this-syntax - (head formals* body* ...))] -``` - -Next, we’ll handle `case-lambda`. As it turns out, expanding `case-lambda` is almost exactly the same as expanding `#%plain-lambda`, except that it has multiple clauses. Since each clause is expanded identically to the body of a `#%plain-lambda`, and it even has the same shape, the clauses can be extracted into a separate syntax class to share code between the two forms: - -```racket -(begin-for-syntax - (define-syntax-class lambda-clause - #:description #f - #:attributes [expansion] - #:commit - [pattern [formals:plain-formals body ...] - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (attribute formals.id) #f intdef-ctx)] - #:with formals* (internal-definition-context-introduce intdef-ctx #'formals) - #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute body))) - #:attr expansion #'[formals* body* ...]])) -``` - -Now, both `#%plain-lambda` and `case-lambda` can be handled in a few lines of code each: - -```racket -[(head:#%plain-lambda ~! . clause:lambda-clause) - (syntax/loc/props this-syntax - (head . clause.expansion))] - -[(head:case-lambda ~! clause:lambda-clause ...) - (syntax/loc/props this-syntax - (head clause.expansion ...))] -``` - -Finally, we need to tackle the three `let` forms. None of these involve any fundamentally new ideas, but they are a little bit more involved than the variants of lambda due to the need to handle the RHSs. Each variant is slightly different, but not dramatically so: the bindings aren’t in scope when expanding the RHSs of `let-values`, but they are for `letrec-values` and `letrec-syntaxes+values`, and `letrec-syntaxes+values` creates transformer bindings and must evaluate some RHSs in phase 1 while `let-values` and `letrec-values` exclusively bind runtime bindings. It would be possible to implement these three forms in separate clauses, but since we’d ideally like to duplicate as little code as possible, we can write a rather elaborate `syntax/parse` pattern to handle all three binding forms all at once. - -We’ll start by handling `let-values` alone to keep things simple: - -```racket -[(head:let-values ~! ([(x:id ...) rhs] ...) body ...) - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx)] - #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...]) - #:with [rhs* ...] (map expand-expression (attribute rhs)) - #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute body))) - (syntax/loc/props this-syntax - (head ([(x* ...) rhs*] ...) body* ...))] -``` - -This isn’t dramatically different from the implementation of `#%plain-lambda`. The only difference is that we have to recursively invoke `expand-expression` on the RHSs in addition to expanding the body expressions. To handle `letrec-values` in the same clause, however, we’ll have to get a little more creative. - -So far, we haven’t actually tapped very far into `syntax/parse`’s pattern language over the course of these two blog posts. The full language available to patterns is rather extensive, and we can take advantage of that to write a modification of the above clause that handles both `let-values` and `letrec-values` at once: - -```racket -[({~or {~and head:let-values {~bind [rec? #f]}} - {~and head:letrec-values {~bind [rec? #t]}}} - ~! ([(x:id ...) rhs] ...) body ...) - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx)] - #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...]) - #:with [rhs* ...] (if (attribute rec?) - (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute rhs))) - (map expand-expression (attribute rhs))) - #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute body))) - (syntax/loc/props this-syntax - (head ([(x* ...) rhs*] ...) body* ...))] -``` - -The `~bind` pattern allows us to explicitly control how attributes are bound as part of the pattern-matching process, which allows us to track when we want to enable the recursive binding behavior of `letrec-values` in our handler code. Since the vast majority of the logic is otherwise identical, this is a significant improvement over duplicating the clause. - -Adding support for `letrec-syntaxes+values` is done in the same general way, but the pattern is even more involved. In addition to tracking whether or not the bindings are recursive, we have to track if any syntax bindings were present at all, and if they were, bind them with `syntax-local-bind-syntaxes`: - -```racket -[({~or {~or {~and head:let-values ~! {~bind [rec? #f] [stxs? #f]}} - {~and head:letrec-values ~! {~bind [rec? #t] [stxs? #f]}}} - {~seq head:letrec-syntaxes+values {~bind [rec? #t] [stxs? #t]} - ~! ([(x/s:id ...) rhs/s] ...)}} - ([(x:id ...) rhs] ...) body ...) - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx) - (when (attribute stxs?) - (for ([xs/s (in-list (attribute x/s))] - [rhs/s (in-list (attribute rhs/s))]) - (syntax-local-bind-syntaxes xs/s rhs/s intdef-ctx)))] - #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...]) - #:with [rhs* ...] (if (attribute rec?) - (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute rhs))) - (map expand-expression (attribute rhs))) - #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute body))) - (if (attribute stxs?) - (~> (syntax/loc this-syntax - (letrec-values ([(x* ...) rhs*] ...) body* ...)) - (syntax-track-origin this-syntax #'head)) - (syntax/loc/props this-syntax - (head ([(x* ...) rhs*] ...) body* ...)))] -``` - -This behemoth clause handles all three varieties of `let` forms that can appear in the result of `local-expand`. Notably, in the `letrec-syntaxes+values` case, we expand into `letrec-values`, since the transformer bindings are effectively erased, and we use `syntax-track-origin` to record that the result originally came from a use of `letrec-syntaxes+values`. - -With these five clauses, we’ve handled all the special forms that can appear in expression position in Racket’s kernel language. To tie things off, we just need to handle the cases of a variable reference, which is represented by a bare identifier not bound to syntax, or literal data, like numbers or strings. We can add one more clause at the end to handle those: - -```racket -[_ - this-syntax] -``` - -Putting them all together, our `expand-expression` function looks as follows: - -```racket -(begin-for-syntax - (define (expand-expression stx) - (syntax-parse (parameterize ([current-context 'expression]) - (current-expand stx)) - #:literal-sets [kernel-literals] - [({~or quote quote-syntax #%top #%variable-reference} ~! . _) - this-syntax] - - [({~and head {~or #%expression #%plain-app begin begin0 if with-continuation-mark}} ~! form ...) - #:with [form* ...] (map expand-expression (attribute form)) - (syntax/loc/props this-syntax - (head form* ...))] - - [(head:#%plain-lambda ~! . clause:lambda-clause) - (syntax/loc/props this-syntax - (head . clause.expansion))] - - [(head:case-lambda ~! clause:lambda-clause ...) - (syntax/loc/props this-syntax - (head clause.expansion ...))] - - [({~or {~or {~and head:let-values ~! {~bind [rec? #f] [stxs? #f]}} - {~and head:letrec-values ~! {~bind [rec? #t] [stxs? #f]}}} - {~seq head:letrec-syntaxes+values {~bind [rec? #t] [stxs? #t]} - ~! ([(x/s:id ...) rhs/s] ...)}} - ([(x:id ...) rhs] ...) body ...) - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx) - (when (attribute stxs?) - (for ([xs/s (in-list (attribute x/s))] - [rhs/s (in-list (attribute rhs/s))]) - (syntax-local-bind-syntaxes xs/s rhs/s intdef-ctx)))] - #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...]) - #:with [rhs* ...] (if (attribute rec?) - (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute rhs))) - (map expand-expression (attribute rhs))) - #:with [body* ...] (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute body))) - (if (attribute stxs?) - (~> (syntax/loc this-syntax - (letrec-values ([(x* ...) rhs*] ...) body* ...)) - (syntax-track-origin this-syntax #'head)) - (syntax/loc/props this-syntax - (head ([(x* ...) rhs*] ...) body* ...)))] - - [_ - this-syntax]))) -``` - -If we try it out, we’ll see that it really does work! Even complicated local binding forms are handled properly by our expander: - -```racket -> (expand-expression - #'(let ([x 42]) - (letrec-syntax ([y (make-rename-transformer #'z)] - [z (make-rename-transformer #'x)]) - (+ y 3)))) -# -``` - -We are now able to expand arbitrary Racket expressions in the same way that the expander does. While this might not seem immediately useful—after all, we haven’t actually gained anything here over just calling `local-expand` with an empty stop list—we can use this as the basis of an expander that can extensibly handle custom core forms, which I may cover in a future blog post. - -# Adding support for internal definitions - -In the previous section, we defined an expander that could expand arbitrary Racket expressions, but our expander is still imperfect: we still do not support internal definitions. For all forms that have bodies, including `#%plain-lambda`, `case-lambda`, `let-values`, `letrec-values`, and `letrec-syntaxes+values`, Racket permits the use of internal definitions. - -In practice, internal-definition contexts allow for an increased degree of modularity compared to traditional local binding forms, since they provide an *extensible* binding language. Users may mix many different binding forms within a single definition context, such as `define`, `define-syntax`, `match-define`, and even `struct`. However, this means the rewriting process described earlier in this blog post is not as simple as detecting the definitions and lifting them into a local binding form, since it’s not immediately apparent which forms are binding forms and which are expressions! - -For this reason, expanding internal-definition contexts happens to be a nontrivial problem in itself. It involves a little more care than expanding expressions does, since it requires using partial expansion to discover which forms are definitions and which forms are expressions. We must take care to never expand too much, but also to expand enough that we reveal all uses of `define-values` and `define-syntaxes` (which all definition forms eventually expand into). We also must handle the splicing behavior of `begin`, which is necessary to allow single forms to expand into multiple definitions. - -We’ll start by writing an `expand-body` function, which operates similarly to our previous `expand-expression` function. Unlike `expand-expression`, `expand-body` will accept a *list* of syntax objects, which represents the sequence of forms that make up the body. Logically, each body will create a first-class definition context with `syntax-local-make-definition-context` to represent the sequence of definitions: - -```racket -(begin-for-syntax - (define (expand-body stxs) - (define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (parameterize ([current-context (list (gensym))] - [current-intdef-ctx intdef-ctx]) - ))) -``` - -The bulk of our `expand-body` function will be a loop that partially expands body forms, adds definitions to the definition context as it discovers them, and returns the expressions and runtime definitions to be rewritten into binding pairs for a `letrec-values` form. Additionally, the loop will also track so-called *disappeared uses* and *disappeared bindings*, which are attached to the expansion using syntax properties to allow tools like DrRacket to learn about the binding structure of phase 1 definitions that are erased as part of macroexpansion. - -The skeleton of this loop is relatively straightforward to write. We will iterate over the syntax objects that make up the body, expand them, and process the expansion using `syntax-parse`: - -```racket -(begin-for-syntax - (define (expand-body stxs) - (define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (parameterize ([current-context (list (gensym))] - [current-intdef-ctx intdef-ctx]) - (define-values [binding-clauses exprs disappeared-uses disappeared-bindings] - (let loop ([stxs stxs] - [binding-clauses '()] - [exprs '()] - [disappeared-uses '()] - [disappeared-bindings '()]) - (if (empty? stxs) - (values (reverse binding-clauses) (reverse exprs) disappeared-uses disappeared-bindings) - (syntax-parse (current-expand (first stxs)) - #:literal-sets [kernel-literals] - ))))))) -``` - -The hard part, of course, is actually handling the potential results of that expansion. We need to handle three forms specially: `begin`, `define-values`, and `define-syntaxes`. All other results of partial expansion will be treated as expressions. We’ll start by handling `begin`, since it’s the simplest case; we only need to prepend the subforms to the list of body forms to be processed, then continue looping: - -```racket -[(head:begin ~! form ...) - (loop (append (attribute form) stxs) binding-clauses exprs - disappeared-uses disappeared-bindings)] -``` - -However, as is often the case, this isn’t quite perfect, since the information that these forms came from a surrounding `begin` is lost, which tools like DrRacket want to know. To solve this problem, the expander adjusts the `origin` property for all spliced forms, which we can mimic using `syntax-track-origin`: - -```racket -[(head:begin ~! form ...) - (loop (append (for/list ([form (in-list (attribute form))]) - (syntax-track-origin form this-syntax #'head)) - stxs) - binding-clauses exprs disappeared-uses disappeared-bindings)] -``` - -This is sufficient for `begin`, so we can move onto the actual definitions themselves. This actually isn’t too hard, since we just need to add the bindings we discover to the first-class definition context and preserve `define-values` bindings as binding pairs: - -```racket -[(head:define-values ~! [x:id ...] rhs) - #:do [(syntax-local-bind-syntaxes (attribute x) #f intdef-ctx)] - (loop (rest stxs) (cons #'[(x ...) rhs] binding-clauses) exprs - disappeared-uses disappeared-bindings)] -``` - -This solution is missing one thing, however, which is the use of `syntax-local-identifier-as-binding` to any use-site scopes that were added to the binding identifier while expanding the binding form in the definition context. Explaining precisely why this is necessary is outside the scope of this blog post, and is best understood by reading [the section on use-site scopes][use-site-scopes] in the paper that describes the theory behind Racket’s current macro system, Bindings as Sets of Scopes. In any case, the impact on our implementation is small: - -```racket -[(head:define-values ~! [x:id ...] rhs) - #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x)) - #:do [(syntax-local-bind-syntaxes (attribute x*) #f intdef-ctx)] - (loop (rest stxs) (cons #'[(x* ...) rhs] binding-clauses) exprs - disappeared-uses disappeared-bindings)] -``` - -Finally, as with `begin`, we want to track that the binding pairs we generate actually came from a use of `define-values` (which in turn likely came from a use of some other definition form). Therefore, we’ll add another use of `syntax-track-origin` to copy and extend the necessary properties: - -```racket -[(head:define-values ~! [x:id ...] rhs) - #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x)) - #:do [(syntax-local-bind-syntaxes (attribute x*) #f intdef-ctx)] - (loop - (rest stxs) - (cons (syntax-track-origin #'[(x* ...) rhs] this-syntax #'head) binding-clauses) - exprs disappeared-uses disappeared-bindings)] -``` - -That’s it for `define-values`. All that’s left is to handle `define-syntaxes`, which is quite similar, but instead of storing the definition in a binding pair, its RHS is immediately evaluated and added to the definition context using `syntax-local-bind-syntaxes`: - -```racket -[(head:define-syntaxes ~! [x:id ...] rhs) - #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x)) - #:do [(syntax-local-bind-syntaxes (attribute x*) #'rhs intdef-ctx)] - (loop (rest stxs) binding-clauses exprs - (cons #'head disappeared-uses) (cons (attribute x*) disappeared-bindings))] -``` - -As the above snippet indicates, this is also where the disappeared uses and disappeared bindings come in. In previous cases, we’ve used `syntax-track-origin` to indicate that a piece of syntax was the result of expanding a different piece of syntax, but in this case, `define-syntaxes` doesn’t expand into anything at all; it’s simply removed from the expansion entirely. Therefore, we need to resort to tracking the information in syntax properties on the resulting `letrec-values` form, so we’ll save them for later. - -Finally, to finish things up, we can add a catchall clause that handles all other forms, which are now guaranteed to be expressions: - -```racket -[_ - (loop (rest stxs) binding-clauses (cons this-syntax exprs) - disappeared-uses disappeared-bindings)] -``` - -This completes our loop that processes definition forms, so all that’s left to do is handle the results. The only significant remaining work is to actually expand the RHSs of the binding pairs we collected and the body expressions, which can be done by calling our own `expand-expression` function directly: - -```racket -(define expanded-binding-clauses - (for/list ([binding-clause (in-list binding-clauses)]) - (syntax-parse binding-clause - [[(x ...) rhs] - (quasisyntax/loc/props this-syntax - [(x ...) #,(expand-expression #'rhs)])]))) -(define expanded-exprs (map expand-expression exprs)) -``` - -Finally, we can assemble all the pieces together into a single local binding form with the appropriate syntax properties: - -```racket -(~> #`(letrec-values #,expanded-binding-clauses #,@expanded-exprs) - (syntax-property 'disappeared-uses disappeared-uses) - (syntax-property 'disappeared-bindings disappeared-bindings)) -``` - -That’s it. We’ve now written an `expand-body` function that can process internal definition contexts in the same way that the macroexpander does. Overall, the whole function is just under 45 lines of code: - -```racket -(begin-for-syntax - (define (expand-body stxs) - (define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (parameterize ([current-context (list (gensym))] - [current-intdef-ctx intdef-ctx]) - (define-values [binding-clauses exprs disappeared-uses disappeared-bindings] - (let loop ([stxs stxs] - [binding-clauses '()] - [exprs '()] - [disappeared-uses '()] - [disappeared-bindings '()]) - (if (empty? stxs) - (values (reverse binding-clauses) (reverse exprs) disappeared-uses disappeared-bindings) - (syntax-parse (current-expand (first stxs)) - #:literal-sets [kernel-literals] - [(head:begin ~! form ...) - (loop (append (for/list ([form (in-list (attribute form))]) - (syntax-track-origin form this-syntax #'head)) - stxs) - binding-clauses exprs disappeared-uses disappeared-bindings)] - [(head:define-values ~! [x:id ...] rhs) - #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x)) - #:do [(syntax-local-bind-syntaxes (attribute x*) #f intdef-ctx)] - (loop - (rest stxs) - (cons (syntax-track-origin #'[(x* ...) rhs] this-syntax #'head) binding-clauses) - exprs disappeared-uses disappeared-bindings)] - [(head:define-syntaxes ~! [x:id ...] rhs) - #:with [x* ...] (map syntax-local-identifier-as-binding (attribute x)) - #:do [(syntax-local-bind-syntaxes (attribute x*) #'rhs intdef-ctx)] - (loop (rest stxs) binding-clauses exprs - (cons #'head disappeared-uses) (cons (attribute x*) disappeared-bindings))] - [_ - (loop (rest stxs) binding-clauses (cons this-syntax exprs) - disappeared-uses disappeared-bindings)])))) - (define expanded-binding-clauses - (for/list ([binding-clause (in-list binding-clauses)]) - (syntax-parse binding-clause - [[(x ...) rhs] - (quasisyntax/loc/props this-syntax - [(x ...) #,(expand-expression #'rhs)])]))) - (define expanded-exprs (map expand-expression exprs)) - (~> #`(letrec-values #,expanded-binding-clauses #,@expanded-exprs) - (syntax-property 'disappeared-uses disappeared-uses) - (syntax-property 'disappeared-bindings disappeared-bindings))))) -``` - -The next step is to actually use this function. We need to replace certain recursive calls to `expand-expression` with calls to `expand-body`, but if we do this naïvely, we’ll have some problems. Currently, when we expand body forms, they’re always immediately inside another definition context (i.e. the bindings introduced by lambda formals or by `let` binding pairs), but they haven’t actually been expanded in that context yet. When we call `expand-body`, we create a nested context, which will inherit the bindings, but won’t automatically add the parent context’s scope. Therefore, we need to manually call `internal-definition-context-introduce` on the body syntax objects before calling `expand-body`. We can write a small helper function to make this easier: - -```racket -(begin-for-syntax - (define (expand-body/in-ctx stxs ctx) - (define (add-ctx-scope stx) - (internal-definition-context-introduce ctx stx 'add)) - (parameterize ([current-intdef-ctx ctx]) - (add-ctx-scope (expand-body (map add-ctx-scope stxs)))))) -``` - -Now we just need to replace the relevant calls to `expand-expression` with calls to `expand-body/in-ctx`, starting with a minor adjustment to our `lambda-clause` syntax class from earlier: - -```racket -(begin-for-syntax - (define-syntax-class lambda-clause - #:description #f - #:attributes [expansion] - #:commit - [pattern [formals:plain-formals body ...] - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (attribute formals.id) #f intdef-ctx)] - #:with formals* (internal-definition-context-introduce intdef-ctx #'formals) - #:with body* (expand-body/in-ctx (attribute body) intdef-ctx) - #:attr expansion #'[formals* body*]])) -``` - -The only other change must occur in the handling of the various `let` forms, which similarly replaces `expand-expression` with `expand-body/in-ctx`: - -```racket -[({~or {~or {~and head:let-values ~! {~bind [rec? #f] [stxs? #f]}} - {~and head:letrec-values ~! {~bind [rec? #t] [stxs? #f]}}} - {~seq head:letrec-syntaxes+values {~bind [rec? #t] [stxs? #t]} - ~! ([(x/s:id ...) rhs/s] ...)}} - ([(x:id ...) rhs] ...) body ...) - #:do [(define intdef-ctx (syntax-local-make-definition-context (current-intdef-ctx))) - (syntax-local-bind-syntaxes (append* (attribute x)) #f intdef-ctx) - (when (attribute stxs?) - (for ([xs/s (in-list (attribute x/s))] - [rhs/s (in-list (attribute rhs/s))]) - (syntax-local-bind-syntaxes xs/s rhs/s intdef-ctx)))] - #:with [[x* ...] ...] (internal-definition-context-introduce intdef-ctx #'[[x ...] ...]) - #:with [rhs* ...] (if (attribute rec?) - (parameterize ([current-intdef-ctx intdef-ctx]) - (map expand-expression (attribute rhs))) - (map expand-expression (attribute rhs))) - #:with body* (expand-body/in-ctx (attribute body) intdef-ctx) - (if (attribute stxs?) - (~> (syntax/loc this-syntax - (letrec-values ([(x* ...) rhs*] ...) body*)) - (syntax-track-origin this-syntax #'head)) - (syntax/loc/props this-syntax - (head ([(x* ...) rhs*] ...) body*)))] -``` - -With these changes, we’ve now extended our expression expander with the ability to expand internal definitions. We can see this in action on a simple example: - -```racket -> (expand-expression - #'(let () - (define x 42) - (define-syntax y (make-rename-transformer #'z)) - (define-syntax z (make-rename-transformer #'x)) - (+ y 3))) -# -``` - -Just as we’d like, the transformer bindings were expanded and subsequently eliminated, and the runtime binding was collected into a `letrec-values` form. The outer `let-values` is left over from the outer `let`, which is needed only to create an internal-definition context to hold our internal definitions. - -# Putting the expression expander to work - -So far, we’ve done a lot of work to emulate the behavior of Racket’s macroexpander, and as the above example demonstrates, we’ve been fairly successful in that goal. However, you might be wondering *why* we did any of this, as replicating the behavior of `local-expand` is not very useful on its own. As mentioned above, this can be used as the foundation of an expander for custom core forms that extends, rather than replaces, the built-in Racket core forms, It can also be used to “cheat” and expand through the behavior of the `local-expand` stop list, which implicitly adds the Racket core forms to any non-empty stop list. Hopefully, I’ll have a chance to cover some of these things more deeply in the future, but for now, I’ll just give a small taste of the latter. - -By using the power of our `expand-expression` function, it’s actually possible to use this kind of expression expander to do genuinely nefarious things, such as hijack the behavior of arbitrary macros! For example, we could do something evil like make `for` loops run in reverse order by adding `for` to `current-stop-list`, then adding an additional special case to `expand-expression` for `for`: - -```racket -(begin-for-syntax - (define current-stop-list (make-parameter (list #'define-values #'define-syntaxes #'for))) - - (define (expand-expression stx) - (syntax-parse (parameterize ([current-context 'expression]) - (current-expand stx)) - #:literal-sets [kernel-literals] - #:literals [for] - ; ... - [(head:for ([x:id seq:expr] ...) body ...+) - (syntax/loc/props this-syntax - (head ([x (in-list (reverse (sequence->list seq)))] ...) - body ...))] - ; ... - ))) -``` - -Amazingly, due to the fact that we’ve taken complete control of the expansion process, this will rewrite uses of `for` *even if they are introduced by macroexpansion*. For example, we could write a small macro that expands into a use of `for`: - -```racket -(define-simple-macro (print-up-to n) - (for ([i (in-range n)]) - (println i))) - -> (print-up-to 5) -0 -1 -2 -3 -4 -``` - -If we write a wrapper macro that applies our evil version of `expand-expression` to its body, then wrap a use of our `print-up-to` macro with it, it will execute the loop in reverse order: - -```racket -(define-syntax-parser hijack-for-loops - [(_ form:expr) (expand-expression #'form)]) - -> (hijack-for-loops - (print-up-to 5)) -4 -3 -2 -1 -0 -``` - -On its own, this is not that impressive, since we could have just used `local-expand` on the body directly to achieve this. However, what’s remarkable about `hijack-for-loops` is that it will work even if the `for` loop is buried deep inside some arbitrary expression: - -```racket -> (define foo - (hijack-for-loops - (lambda (x) - (define n (* x 2)) - (print-up-to n)))) -> (foo 3) -5 -4 -3 -2 -1 -0 -``` - -Of course, this example is rather contrived—mucking with `for` loops like this isn’t useful at all, and nobody would really write `print-up-to` as a macro, anyway—but there is potential for using this technique to do more interesting things. - -# Closing thoughts - -The system outlined in this blog post is not something I would recommend using in any real macro. It is enormously complicated, requires knowledge well above that of your average working macrologist, and it involves doing rather horrible things to the macro system, things it was undoubtably never designed to do. Still, I believe this blog post is useful, for a few different reasons: - - 1. The technology outlined in this post, while perhaps not directly applicable to existing real-world problems, provides a framework for implementing various new kinds of syntax transformations in Racket *without* extending the macro system. It demonstrates the expressive power of the macro system, and it hopefully lays the foundation for a better, more high-level interface for users who wish to define their own languages with custom core forms. - - 2. This system provides insight into the way the Racket macroexpander operates, *in terms of the userspace syntax API*. The canonical existing model of hygienic macroexpansion, in the aforementioned [Bindings as Sets of Scopes][scope-sets] paper, does not explain the workings of internal definition contexts in detail, and it certainly doesn’t explain them in terms that a Racket programmer would already be familiar with. By reencoding those ideas within the macro system itself, an advanced macro writer may be able to more easily connect concepts in the macro system’s implementation to concepts they have already been exposed to. - - 3. The capability of the proof-of-concept outlined here demonstrates that the limitation imposed by the existing implementation of the stop list (namely, the way it is implicitly extended with additional identifiers) is essentially artificial, and it can be hacked around with sufficient (albeit significant) effort. This isn’t enormously important, but it is somewhat relevant to a recent debate in [a GitHub issue][gh-issue-2154] about the handling of the `local-expand` stop list. - - 4. Finally, for myself as much as anyone else, this implementation records in a concise way (perhaps overly concise at times) the collection of very subtle details I’ve learned over the past six months about how information is preserved and propagated during the expansion process. - -This blog post is not for everybody. If you made it to the end, give yourself a pat on the back. If you made it to the end *and* understood everything you read: congratulations, you are a certified expert in Racket macro programming. If not, do not fear, and do not lose hope—I plan for something significantly more mellow next time. - -As always, I’d like to give thanks to the people who contributed significantly, if indirectly, to the contents of this blog post, namely [Matthew Flatt][mflatt], [Michael Ballantyne][mballantyne], and [Ryan Culpepper][ryanc]. And finally, for those interested, all of the code in this blog post can be found in a runnable form [in this GitHub gist][expression-expander-source]. - - -[custom-core-forms-1]: /blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/ -[expression-expander-source]: https://gist.github.com/lexi-lambda/c4f4b91ac9c0a555447d72d02e18be7b -[gh-issue-2154]: https://github.com/racket/racket/issues/2154 -[mflatt]: http://www.cs.utah.edu/~mflatt/ -[mballantyne]: http://mballantyne.net -[ryanc]: http://www.ccs.neu.edu/home/ryanc/ -[scope-sets]: http://www.cs.utah.edu/plt/scope-sets/ -[use-site-scopes]: http://www.cs.utah.edu/plt/scope-sets/pattern-macros.html#%28part._use-site%29 diff --git a/blog/posts/2018-10-06-macroexpand-anywhere-with-local-apply-transformer.md b/blog/posts/2018-10-06-macroexpand-anywhere-with-local-apply-transformer.md deleted file mode 100644 index dd49986..0000000 --- a/blog/posts/2018-10-06-macroexpand-anywhere-with-local-apply-transformer.md +++ /dev/null @@ -1,278 +0,0 @@ - Title: Macroexpand anywhere with `local-apply-transformer`! - Date: 2018-10-06T22:34:46 - Tags: racket, macros - -Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only *certain positions* in Racket code are subject to macroexpansion. - -To illustrate, consider a macro that provides a Clojure-style `let` form: - -```racket -(require syntax/parse/define) - -(define-simple-macro (clj-let [{~seq x:id e:expr} ...] body:expr ...+) - (let ([x e] ...) body ...)) -``` - -This can be used anywhere an expression is expected, and it does as one would expect: - -```racket -> (clj-let [x 1 - y 2] - (+ x y)) -3 -``` - -However, a novice macro programmer might realize that `clj-let` really only modifies the syntax of *binding pairs* for a `let` form. Therefore, could one define a macro that only adjusts the binding pairs of some existing `let` form instead of expanding to an entire `let`? That is, could one write the above example like this: - -```racket -(define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...]) - ([x e] ...)) - -> (let (clj-binding-pairs - [x 1 - y 2]) - (+ x y)) -3 -``` - -The answer is *no*: the binding pairs of a `let` form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion *anywhere* in a Racket program. - -# Why only some positions are subject to macroexpansion - -To understand *why* the macroexpander refuses to touch certain positions in a program, we must first understand how the macro system operates. In Racket, a macro is defined as a compile-time function associated with a particular binding, and macros are given complete control over the syntax trees they are surrounded with. If we define a macro *`mac`*, then we write the expression (*mac* *form*), *`form`* is provided as-is to *`mac`* as a syntax object. Its structure can be anything at all, since *`mac`* can be an arbitrary Racket function, and that function can use *`form`* however it pleases. - -To give a concrete illustration, consider a macro that binds some identifiers to symbols in a local scope: - -```racket -(define-simple-macro (let-symbols (x:id ...) body ...+) - (let ([x 'x] ...) body ...)) - -> (let-symbols (hello goodbye) - (list hello goodbye)) -'(hello goodbye) -``` - -It isn’t the most exciting macro in the world, but it illustrates a key point: the first subform to `let-symbols` is a list of identifiers that are eventually put in *binding* position. This means that `hello` and `goodbye` are bindings, not uses, and such bindings shadow any existing bindings that might have been in scope: - -```racket -> (let ([foo 42]) - (let-symbols (foo) - foo)) -'foo -``` - -This might not seem very interesting, but it’s critical to understand, since it means that the expander *can’t know* which sub-pieces of a use of `let-symbols` will eventually be expressions themselves until it expands the macro and discovers it produces a `let` form, so it can’t know where it’s safe to perform macroexpansion. To make this more explicit, imagine we define a macro under some name, then try and use that name with our `let-symbols` macro: - -```racket -(define-simple-macro (hello x:id) - (x:id)) - -> (let-symbols (hello goodbye) - hello) -``` - -What should the above program do? If we treat the first use of `hello` in the `let-symbols` form as a macro application, then `(hello goodbye)` should be transformed into `(goodbye)`, and the use of `hello` in the body should be a syntax error. But if the first use of `hello` was instead intended to be a binder, then it should shadow the `hello` definition above, and the output of the program should be `'hello`. - -To avoid the chaos that would ensue if defining a macro could completely break local reasoning about other macros, Racket chooses the second option, and the program produces `'hello`. The macroexpander has no way of knowing *how* each macro will inspect its constituent pieces, so it avoids touching anything until the macro expands. After it discovers the `let` form in the expansion of `let-symbols`, it can safely determine that the body expressions are, indeed, expressions, and it can recursively expand any macros they contain. To put things another way, a macro’s sub-forms are never expanded before the macro itself is expanded, only after. - -# Forcing sub-form expansion - -The above section explains why the expander must operate as it does, but it’s a little bit unsatisfying. What if we write a macro where we *want* certain sub-forms to be expanded before they are passed to us? Fortunately, the Racket macro system provides an API to handle this use case, too. - -It is true that the Racket macro system never *automatically* expands sub-forms before outer forms are expanded, but macro transformers can explicitly op-in to recursive expansion via the [`local-expand`][local-expand] function. This function effectively yields control back to the expander to expand some arbitrary piece of syntax as an expression, and when it returns, the macro transformer can inspect the expanded expression however it wishes. In theory, this can be used to implement extensible macros that allow macroexpansion in locations other than expression position. - -To give an example of such a macro, consider the Racket `match` form, which implements an expressive pattern-matcher as a macro. One of the most interesting qualities of Racket’s `match` macro is that its pattern language is user-extensible, essentially allowing pattern-level macros. For example, a user might find they frequently match against natural numbers, and they wish to be able to write `(nat n)` as a shorthand for `(? exact-nonnegative-integer? n)`. Fortunately, this is easy using `define-match-expander`: - -```racket -(define-match-expander nat - (syntax-parser - [(_ pat) - #'(? exact-nonnegative-integer? pat)])) - -> (match '(-5 -2 4 -7) - [(list _ ... (nat n) _ ...) - n]) -4 -``` - -Clearly, `match` is somehow expanding the `nat` match expander as a part of its expansion. Is it using `local-expand`? - -Well, no. While [a previous blog post of mine][custom-core-forms-1] has illustrated that it is possible to do such a thing with `local-expand` via some clever trickery, `local-expand` is really designed to expand *expressions*. This is a problem, since `(nat n)` is not an expression, it’s a pattern: it will expand into `(? exact-nonnegative-integer? n)`, which will lead to a syntax error, since `?` is not bound in the world of expressions. Instead, for a long while, `match` and forms like it have emulated how the expander performs macroexpansion in ad-hoc ways. Fortunately, as of Racket v7.0, the new [`local-apply-transformer`][local-apply-transformer] API provides a way to invoke recursive macroexpansion in a consistent way, and it doesn’t assume that what’s being expanded is an expression. - -## A closer look at `local-apply-transformer` - -If `local-apply-transformer` is the answer, what does it actually do? Well, `local-apply-transformer` allows explicitly invoking a transformer function on some piece of syntax and retrieving the result. In other words, `local-apply-transformer` allows expanding an arbitrary macro, but since it doesn’t make any assumptions about what the output will be, it only expands it *once*: just a single step of macro transformation. - -To illustrate, we can write a macro that uses `local-apply-transformer` to invoke a transformer function and preserve the result using `quote-syntax`: - -```racket -(require (for-syntax syntax/apply-transformer)) - -(define-for-syntax flip - (syntax-parser - [(a b more ...) - #'(b a more ...)])) - -(define-simple-macro (mac) - #:with result (local-apply-transformer flip #'(([x 1]) let x) 'expression) - (quote-syntax result)) -``` - -When we use `mac`, our `flip` function will be applied, as a macro, to the syntax object we provide: - -```racket -> (mac) -# -``` - -Alright, so this works, but it raises some questions. Why is `flip` defined as a function at phase 1 (using `define-for-syntax`) instead of as a macro (using `define-syntax`)? What’s the deal with the `'expression` argument to `local-apply-transformer` given that `local-apply-transformer` is supposedly decoupled from expression expansion? And finally, how is this any different from just calling our `flip` function on the syntax object directly by writing `(flip #'(([x 1]) let x))`? - -Let’s start with the first of those questions: why is `flip` defined as a function rather than as a macro? Well, `local-apply-transformer` is a fairly low-level operation: remember, it doesn’t assume *anything* about the argument it’s given! Therefore, it doesn’t take an expression containing a macro and expand it based on its structure, it needs to be explicitly provided the macro transformer function to apply. In practice, this might not seem very useful, since presumably we want to write our macros as macros, not as phase 1 functions. Fortunately, it’s possible to look up the function associated with a macro binding using the [`syntax-local-value`][syntax-local-value] function, so if we use that, we can define `flip` using `define-syntax` as usual: - -```racket -(define-syntax flip - (syntax-parser - [(a b more ...) - #'(b a more ...)])) - -(define-simple-macro (mac) - #:with result (local-apply-transformer (syntax-local-value #'flip) - #'(([x 1]) let x) - 'expression) - (quote-syntax result)) -``` - -Now for the next question: what is the meaning of the `'expression` argument? This one is more of a historical artifact than anything else: when the expander applies a macro transformer, it does it in a “context”, which is accessible using the [`syntax-local-context`][syntax-local-context] function. This context can be one of a predefined enumeration of cases, including `'expression`, `'top-level`, `'module`, `'module-begin`, or a list representing a definition context. Whether or not any of those actually apply to our use case, we still have to pick one, but aside from how they affect the value returned by `syntax-local-context` (which some macros inspect), the value we choose is largely irrelevant. Using `'expression` will do, even if it’s a bit of a lie. - -Finally, how does any of this differ from just applying the function we get directly? Well, the critical answer is all about *hygiene*. Racket’s macro system is hygienic, which, among other things, ensures bindings defined with the same name in different places do not unintentionally conflict. Racket’s hygiene mechanism is implemented in the macroexpander, when macro transformers are applied. If we just applied the `flip` transformer procedure to a syntax object directly, we would circumvent this hygiene mechanism, potentially causing all sorts of problems. By using `local-apply-transformer`, we ensure hygiene is preserved. - -There is one small problem left with our program, however. Can you spot it? The key is to consider what would happen if we used `flip` as an ordinary macro, without using `local-apply-transformer`: - -```racket -> (flip (([x 1]) let x)) -let: bad syntax - in: let -``` - -What happened? Well, remember that when a macro in Racket is used, it receives the whole use site as a syntax object: in this case, `#'(flip (([x 1]) let x))`. This means that `flip` ought to be written to parse its argument slightly differently: - -```racket -(define-syntax flip - (syntax-parser - [(_ (a b more ...)) - #'(b a more ...)])) -``` - -Indeed, now that we’ve properly restructured the macro, we can easily switch to using the convenient `define-simple-macro` shorthand: - -```racket -(define-simple-macro (flip (a b more ...)) - (b a more ...)) -``` - -This means we also need to update our definition of `mac` to provide the full syntax object the expander would: - -```racket -(define-simple-macro (mac) - #:with result (local-apply-transformer (syntax-local-value #'flip) - #'(flip (([x 1]) let x)) - 'expression) - (quote-syntax result)) -``` - -This might seem redundant, but remember, `local-apply-transformer` is very low-level! While the convention that (*mac* . \_) is the syntax for a macro transformation might seem obvious, `local-apply-transformer` makes no assumptions. It just does what we tell it to do. - -## Applying `local-apply-transformer` - -So what does `local-apply-transformer` have to do with the problem at the beginning of this blog post? Well, as it happens, we can use `local-apply-transformer` to implement a macro that allows expansion *anywhere* using some simple tricks. While it’s true that we cannot magically divine which locations ought to be expanded, what we *can* do is explicitly annotate which places to expand. - -To do this, we will implement a macro, `expand-inside`, that looks for subforms annotated with a special `$expand` identifier and performs macro transformation on those locations before proceeding with ordinary macroexpansion. Using the `clj-binding-pairs` example from the beginning of this blog post, our solution to that problem will look like this: - -```racket -(define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...]) - ([x e] ...)) - -> (expand-inside - (let ($expand - (clj-binding-pairs - [x 1 - y 2])) - (+ x y))) -3 -``` - -Put another way, `expand-inside` will force eager expansion on any subform surrounded with an `$expand` annotation. - -We’ll start by defining the `$expand` binding itself. This binding won’t mean anything at all outside of `expand-inside`, but we’d like it to be a unique binding so that users can rename it (using, `rename-in`, for example) if they wish. To do this, we’ll use the usual trick of defining it as a macro that always produces an error if it’s ever used: - -```racket -(define-syntax ($expand stx) - (raise-syntax-error #f "illegal outside an ‘expand-inside’ form" stx)) -``` - -Next, we’ll implement a syntax class that will form the bulk of our implementation of `expand-inside`. Since we need to find uses of `$expand` that might be deeply-nested inside the syntax object provided to `expand-inside`, we need to recursively look through the syntax object, find any instances of `$expand`, and put it all back together once we’re done. This can be done relatively cleanly using a recursive syntax class: - -```racket -(begin-for-syntax - (define-syntax-class do-expand-inside - #:literals [$expand] - #:attributes [expansion] - [pattern {~or $expand ($expand . _)} - #:with :do-expand-inside (do-$expand this-syntax)] - [pattern (a:do-expand-inside . b:do-expand-inside) - #:attr expansion - (let ([reassembled (cons (attribute a.expansion) - (attribute b.expansion))]) - (if (syntax? this-syntax) - (datum->syntax this-syntax reassembled - this-syntax this-syntax) - reassembled))] - [pattern _ #:attr expansion this-syntax])) -``` - -There are some tricky details to get right in the reassembly of pairs, since syntax lists are actually composed of ordinary pairs rather than syntax pairs, but ultimately, the code for walking a syntax object is small. The key case of this syntax class is the call to `do-$expand` in the first clause, which we have not yet defined. This function will actually handle performing the expansion by invoking `local-apply-transformer`: - -```racket -(begin-for-syntax - (define (do-$expand stx) - (syntax-parse stx - [(_ {~and form {~or trans (trans . _)}}) - #:declare trans (static (disjoin procedure? set!-transformer?) - "syntax transformer") - (local-apply-transformer (attribute trans.value) - #'form - 'expression)]))) -``` - -This uses the handy `static` syntax class that comes with `syntax/parse`, which implicitly handles the call to `syntax-local-value` and produces a nice error message if the value returned does not match a predicate. All we have to do is apply the transformer value bound to the `trans.value` attribute using `local-apply-transformer`, and now the `expand-macro` can be written in just a couple lines of code: - -```racket -(define-syntax-parser expand-inside - #:track-literals - [(_ form:do-expand-inside) #'form.expansion]) -``` - -(Using the `#:track-literals` option, also new in Racket v7.0, ensures that Check Syntax will be able to recognize the uses of `$expand` that disappear from after `expand-inside` is expanded.) - -Putting everything together, our example from above really works: - -```racket -(define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...]) - ([x e] ...)) - -> (expand-inside - (let ($expand - (clj-binding-pairs - [x 1 - y 2])) - (+ x y))) -3 -``` - -That’s it. All told, the entire implementation is only about 30 lines of code. For a full, compilable, working example, see [this gist](https://gist.github.com/lexi-lambda/65d69043023b519694f50dfca2dc7d33). - -[custom-core-forms-1]: /blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/ -[local-apply-transformer]: http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Fapply-transformer..rkt%29._local-apply-transformer%29%29 -[local-expand]: http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29 -[syntax-local-context]: http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-context%29%29 -[syntax-local-value]: http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29 diff --git a/blog/posts/2019-04-21-defeating-racket-s-separate-compilation-guarantee.md b/blog/posts/2019-04-21-defeating-racket-s-separate-compilation-guarantee.md deleted file mode 100644 index 2fe3989..0000000 --- a/blog/posts/2019-04-21-defeating-racket-s-separate-compilation-guarantee.md +++ /dev/null @@ -1,523 +0,0 @@ - Title: Defeating Racket’s separate compilation guarantee - Date: 2019-04-21T05:30:00 - Tags: racket, macros - -Being a self-described [programming-language programming language][manifesto-pl-pl] is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate [composable and compilable macros][macromod]. One of the module system’s foundational properties is its [*separate compilation guarantee*][separate-compilation-guarantee], which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is *essential* for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it. - -Yet today, in this blog post, we’re going to do exactly that. - -# What is the separate compilation guarantee? - -Before we get to the fun part (i.e. breaking things), let’s go over some fundamentals so we understand what we’re breaking. The authoritative source for the separate compilation guarantee is [the Racket reference][separate-compilation-guarantee], but it is dense, as authoritative sources tend to be. Although I enjoy reading technical manuals for sport, it is my understanding that not all the people who read this blog are as strange as I am, so let’s start with a quick primer, instead. (If you’re already an expert, feel free to [skip to the next section](#section:main-start).) - -Racket is a macro-enabled programming language. In Racket, a macro is a user-defined, code-to-code transformation that occurs at compile-time. These transformations cannot make arbitrary changes to the program—in Racket, they are usually required to be *local*, affecting a single expression or definition at a time—but they may be implemented using arbitrary code. This means that a macro can, if it so desires, read the SSH keys off your filesystem and issue an HTTP request to send them someplace. - -That kind of attack is bad, admittedly, but it’s also *uninteresting*: Racket allows you do all that and then some, making no attempt to prevent it.[^1] Racket calls these “external effects,” things that affect state outside of the programming language. They sound scary, but in practice, *internal effects*—effects that mutate state inside the programming language—are a much bigger obstacle to practical programming. Let’s take a look at why. - -Let’s say we have a module with some global, mutable state. Perhaps it is used to keep track of a set of delicious foods: - -```racket -;; foods.rkt -#lang racket -(provide delicious-food? add-delicious-food!) - -(define delicious-foods (mutable-set)) - -(define (delicious-food? food) - (set-member? delicious-foods food)) - -(define (add-delicious-food! new-food) - (set-add! delicious-foods new-food)) -``` - -Using this interface, let’s write a program that checks if a particular food, given as a command-line argument, is delicious: - -```racket -;; check-food.rkt -#lang racket -(require "foods.rkt") - -(add-delicious-food! "pineapple") -(add-delicious-food! "sushi") -(add-delicious-food! "cheesecake") - -(command-line - #:args [food-to-check] - (if (delicious-food? food-to-check) - (printf "~a is a delicious food.\n" food-to-check) - (printf "~a is not delicious.\n" food-to-check))) -``` -```sh -$ racket check-food.rkt cheesecake -cheesecake is a delicious food. -$ racket check-food.rkt licorice -licorice is not delicious. -``` - -Exhilarating. (Sorry, licorice fans.) But what if a *macro* were to call `add-delicious-food!`? What would happen? For example, what if we wrote a macro to add a lot of foods at once?[^2] - -```racket -(require syntax/parse/define) -(define-simple-macro (add-food-combinations! [fst:string ...] - [snd:string ...]) - #:do [(for* ([fst-str (in-list (syntax->datum #'[fst ...]))] - [snd-str (in-list (syntax->datum #'[snd ...]))]) - (add-delicious-food! (string-append fst-str " " snd-str)))] - (void)) - -; should add “fried chicken,” “roasted chicken”, “fried potato,” and “roasted potato” -(add-food-combinations! ["fried" "roasted"] ["chicken" "potato"]) -``` - -Now, what do you think executing `racket check-food.rkt 'fried chicken'` will do? - -Clearly, the program should print `fried chicken is a delicious food`, and indeed, many traditional Lisp systems would happily produce such a result. After all, running `racket check-food.rkt 'fried chicken'` must load the source code inside `check-food.rkt`, expand and compile it, then run the result. While the program is being expanded, the compile-time calls to `add-delicious-food!` should add new elements to the `delicious-food` set, so when the program is executed, the string `"fried chicken"` ought to be in it. - -But if you actually try this yourself, you will find that *isn’t* what happens. Instead, Racket rejects the program: - -```sh -$ racket check-food.rkt 'fried chicken' -check-food.rkt:12:11: add-delicious-food!: reference to an unbound identifier - at phase: 1; the transformer environment - in: add-delicious-food! -``` - -Why does Racket reject this program? Well, consider that Racket allows programs to be pre-compiled using `raco make`, doing all the work of macroexpansion and compilation to bytecode ahead of time. Subsequent runs of the program will use the pre-compiled version, without having to run all the macros again. This is a problem, since expanding the `add-food-combinations!` macro had side-effects that our program depended on! - -If Racket allowed the above program, it might do different things depending on whether it was pre-compiled. Running directly from source code might treat `'fried chicken'` as a delicious food, while running from pre-compiled bytecode might not. Racket considers this unacceptable, so it disallows the program entirely. - -## Preserving separate compilation via phases - -Hopefully, you are now mostly convinced that the above program is a bad one, but you might have some lingering doubts. You might, for example, wonder if Racket disallows mutable compile-time state entirely. That is not the case—Racket really does allow everything that happens at runtime to happen at compile-time—but it does prevent compile-time and run-time state from ever *interacting*. Racket stratifies every program into a compile-time part and a run-time part, and it restricts communication between them to limited, well-defined channels (mainly via expanding to code that does something at run-time). - -Racket calls this system of stratification *phases*. Code that executes at run-time belongs to the *run-time phase*, while code that executes at compile-time (i.e. macros) belongs to the *compile-time phase*. When a variable is defined, it is always defined in a particular phase, so bindings declared with `define` can only be used at run-time, while bindings declared with `define-for-syntax` can only be used at compile-time. Since `add-delicious-food!` was declared using `define`, it was not allowed (and in fact was not even visible) in the body of the `add-food-combinations!` macro. - -While the whole macro system could work precisely as just described, such a strict stratification would be incredibly rigid. Since every definition would belong to either run-time or compile-time, but never both, reusing run-time code to implement macros would be impossible. While the example in the previous section might make it seem like that’s a good thing, it very often isn’t: imagine if general-purpose functions like `map` and `filter` all needed to be written twice! - -To avoid this problem, Racket allows modules to be imported at both run-time and compile-time, so long as it’s done explicitly. Writing `(require "some-library.rkt")` requires `some-library.rkt` for run-time code, but writing `(require (for-syntax "some-library.rkt"))` requires it for compile-time code. Requiring a module `for-syntax` is sort of like implicitly adjusting all of its uses of `define` to be `define-for-syntax`, instead, effectively shifting all the code from run-time to compile-time. This kind of operation is therefore known as *phase shifting* in Racket terminology. - -We can use phase shifting to make the program we wrote compile. If we adjust the `require` at the beginning of our program, then we can ensure `add-delicious-food!` is visible to both the run-time and compile-time parts of `check-food.rkt`: - -```racket -(require "foods.rkt" (for-syntax "foods.rkt")) -``` - -Now our program compiles. However, if you’ve been following everything carefully, you should be wondering why! According to the last section, sharing state between run-time and compile-time fundamentally can’t work without introducing inconsistencies between uncompiled and pre-compiled code. And that’s true—such a thing would cause all sorts of problems, and Racket doesn’t allow it. If you run the program, whether pre-compiled or not, you’ll find it always does the same thing: - -```sh -$ racket check-food.rkt 'fried chicken' -fried chicken is not delicious. -``` - -This seems rather confusing. What happened to the calls to `add-delicious-food!` inside our `add-food-combinations!` macro? If we stick a `printf` inside `add-delicious-food!`, we’ll find that it really does get called: - -```racket -(define (add-delicious-food! new-food) - (printf "Registering ~a as a delicious food.\n" new-food) - (set-add! delicious-foods new-food)) -``` -```sh -$ racket check-food.rkt 'fried chicken' -Registering fried chicken as a delicious food. -Registering fried potato as a delicious food. -Registering roasted chicken as a delicious food. -Registering roasted potato as a delicious food. -Registering pineapple as a delicious food. -Registering sushi as a delicious food. -Registering cheesecake as a delicious food. -fried chicken is not delicious. -``` - -And in fact, if we pre-compile `check-food.rkt`, we’ll see that the first four registrations appear at compile-time, exactly as we expect: - -```sh -$ raco make check-food.rkt -Registering fried chicken as a delicious food. -Registering fried potato as a delicious food. -Registering roasted chicken as a delicious food. -Registering roasted potato as a delicious food. -$ racket check-food.rkt 'fried chicken' -Registering pineapple as a delicious food. -Registering sushi as a delicious food. -Registering cheesecake as a delicious food. -fried chicken is not delicious. -``` - -The compile-time registrations really are happening, but Racket is automatically restricting the compile-time side-effects so they only apply at compile-time. After compilation has finished, Racket ensures that compile-time side effects are thrown away, and the run-time code starts over with fresh, untouched state. This guarantees consistent behavior, since it becomes impossible to distinguish at run-time whether a module was just compiled on the fly, or if it was pre-compiled long ago (possibly even on someone else’s computer). - -This is the essence of the separate compilation guarantee. To summarize: - - - Run-time and compile-time are distinct *phases* of execution, which cannot interact. - - - Modules can be required at multiple phases via *phase shifting*, but their state is kept separate. Each phase gets its own copy of the state. - - - Ensuring that the state is kept separate ensures predictable program behavior, no matter when the program is compiled. - -This summary is a simplification of phases in Racket. The full Racket module system does not have only two phases, since macros can also be *used* at compile-time to implement other macros, effectively creating a separate “compile-time” for the compile-time code. Each compile-time pass is isolated to its own phase, creating a finite but arbitrarily large number of distinct program phases (all but one of which occur at compile-time). - -Furthermore, the separate compilation guarantee does not just isolate the state of each phase from the state of other phases but also isolates all compile-time state from the compile-time state of other modules. This ensures that compilation is still deterministic even if modules are compiled in a different *order*, or if several modules are sometimes compiled individually while other times compiled together all at once. - -If you want to learn more, the full details of the module system are described at length in the [General Phase Levels][guide-general-phase-levels] section of the Racket Guide, but the abridged summary I’ve described is enough for the purposes of this blog post. If the bulleted list above mostly made sense to you, you’re ready to move on. - -

                                            How we’re going to break it

                                            - -The separate compilation guarantee is a sturdy opponent, but it is not without weaknesses. Although no API in Racket, safe or unsafe, allows arbitrarily disabling phase separation, a couple features of Racket are already known to allow limited forms of cross-phase communication. - -The most significant of these, and the one we’ll be using as our vector of attack, is the logger. Unlike many logging systems, which are exclusively string-oriented, Racket’s logging interface allows structured logging by associating an arbitrary Racket value with each and every log message. Since it is possible to set up listeners within Racket that receive log messages sent to a particular “topic,” the logger can be used as a communication channel to send values between different parts of a program. - -The following program illustrates how this works. One thread creates a listener for all log messages on the topic `'send-me-a-value` using `make-log-receiver`, then uses `sync` to block until a value is received. Meanwhile, a second thread sends values through the logger using `log-message`. Together, this creates a makeshift buffered, asynchronous channel: - -```racket -;; log-comm.rkt -#lang racket - -(define t1 - (thread - (lambda () - (define recv (make-log-receiver (current-logger) 'debug 'send-me-a-value)) - (let loop () - (println (sync recv)) - (loop))))) - -(define t2 - (thread - (lambda () - (let loop ([n 0]) - (log-message (current-logger) 'debug 'send-me-a-value "" n #f) - (sleep 1) - (loop (add1 n)))))) - -(thread-wait t1) ; wait forever -``` -``` -$ racket log-comm.rkt -'#(debug "" 1 send-me-a-value) -'#(debug "" 2 send-me-a-value) -'#(debug "" 3 send-me-a-value) -'#(debug "" 4 send-me-a-value) -^Cuser break -``` - -In this program, the value being sent through the logger is just a number, which isn’t very interesting. But the value really can be *any* value, even arbitrary closures or mutable data structures. It’s even possible to send a [channel][guide-channels] through a logger, which can subsequently be used to communicate directly, without having to abuse the logger. - -Generally, this feature of loggers isn’t very useful, since Racket has plenty of features for cross-thread communication. What’s special about the logger, however, is that it is global, and it is cross-phase. - -The cross-phase nature of the logger makes some sense. If a Racket program creates a namespace (that is, a fresh environment for dynamic evaluation), then uses it to expand and compile a Racket module, the process of compilation might produce some log messages, and the calling thread might wish to receive them. It wouldn’t be a very useful logging system if log messages during compile-time were always lost. However, this convenience is a loophole in the phase separation system, since it allows values to flow—bidirectionally—between phases. - -This concept forms the foundation of our exploit, but it alone is not a new technique, and I did not discover it. However, all existing uses I know of that use the logger for cross-phase communication require control of the parent namespace in which modules are being compiled, which means some code must exist “outside” the actual program. That technique does not work for ordinary programs run directly with `racket` or compiled directly with `raco make`, so to get there, we’ll need something more clever. - -## The challenge - -Our goal, therefore, is to share state between phases *without* controlling the compilation namespace. More precisely, we want to be able to create an arbitrary module-level definition that is *cross-phase persistent*, which means it will be evaluated once and only once no matter how many times its enclosing module is re-instantiated (i.e. given a fresh, untouched state) at various phases. A phase-shifted `require` of the module that contains the definition should share state with an unshifted version of the module, breaking the separate compilation guarantee wide open. - -To use the example from the previous section, we should be able to adjust `foods.rkt` very slightly… - -```racket -;; foods.rkt -#lang racket -(require "define-cross-phase.rkt") -(provide delicious-food? add-delicious-food!) - -; share across phases -(define/cross-phase delicious-foods (mutable-set)) - -#| ... |# -``` - -…and the `delicious-foods` mutable state should magically become cross-phase persistent. When running `check-food.rkt` from source, we should see the side-effects persisted from the module’s compilation, while running from pre-compiled bytecode should give us the result with compile-time effects discarded. - -We already know the logger is going to be part of our exploit, but implementing `define/cross-phase` on top of it is more subtle than it might seem. In our previous example that used `make-log-receiver`, we had well-defined sender and receiver threads, but who is the “sender” in our multi-phased world? And what exactly is the sender sending? - -To answer those questions, allow me to outline the general idea of our approach: - - 1. The first time our `foods.rkt` module is instantiated, at any phase, it evaluates the `(mutable-set)` expression to produce a new mutable set. It spawns a sender thread that sends this value via the logger to anyone who will listen, and that thread lingers in the background for the remaining duration of the program. - - 2. All subsequent instantiations of `foods.rkt` do *not* evaluate the `(mutable-set)` expression. Instead, they obtain the existing set by creating a log receiver and obtaining the value the sender thread is broadcasting. This ensures that a single value is shared across all instantiations of the module. - -This sounds deceptively simple, but the crux of the problem is how to determine whether `foods.rkt` has previously been instantiated or not. Since we can only communicate across phases via the logger, we cannot use any shared state to directly record the first time the module is instantiated. We can listen to a log receiver and wait to see if we get a response, but this introduces a race condition: how long do we wait until giving up and deciding we’re the first instantiation? Worse, what if two threads instantiate the module at the same time, and both threads end up spawning a new sender thread, duplicating the state? - -The true challenge, therefore, is to develop a protocol by which we can be *certain* we are the first instantiation of a module, without relying on any unspecified behavior, and without introducing any race conditions. This is possible, but it isn’t obvious, and it requires combining loggers with some extra tools available to the Racket programmer. - -## The key idea - -It’s finally time to tackle the key idea at the heart of our exploit: garbage collection. In Racket, garbage collection is an observable effect, since Racket allows attaching finalizers to values via [wills and executors][reference-wills-and-executors]. Since a single heap is necessarily shared by the entire VM, behavior happening on other threads (even in other phases) can be indirectly observed by creating a unique value—a “canary”—then sending it to another thread, and waiting to see if it will be garbage collected or not (that is, whether or not the canary “dies”). - -Remember that logs and log receivers are effectively buffered, multicast, asynchronous FIFO channels. Since they are buffered, if any thread is already listening to a logger topic when a value is sent, it cannot possibly be garbage collected until that thread either reads it and discards it or the receiver itself is garbage collected. It’s possible to use this mechanism to observe whether or not another thread is already listening on a topic, as the following program demonstrates:[^3] - -```racket -;; check-receivers.rkt -#lang racket - -(define (check-receivers topic) - (define executor (make-will-executor)) - ; limit scope of `canary` so we don’t retain a reference - (let () - (define canary (gensym 'canary)) - (will-register executor canary void) - (log-message (current-logger) 'debug topic "" canary #f)) - (if (begin - (collect-garbage) - (collect-garbage) - (collect-garbage) - (sync/timeout 0 executor)) - (printf "no receivers for ~v\n" topic) - (printf "receiver exists for ~v\n" topic))) - -; add a receiver on topic 'foo -(define recv (make-log-receiver (current-logger) 'debug 'foo)) - -(define t1 (thread (λ () (check-receivers 'foo)))) -(define t2 (thread (λ () (check-receivers 'bar)))) - -(thread-wait t1) -(thread-wait t2) -``` -``` -$ racket check-receivers.rkt -no receivers for 'bar -receiver exists for 'foo -``` - -However, this program has some problems. For one, it needs to call `collect-garbage` several times to be certain that the canary will be collected if there are no listeners, which can take a second or two, and it also assumes that three calls to `collect-garbage` will be enough to collect the canary, though there is no guarantee that will be true. - -A bulletproof solution should be both reasonably performant and guaranteed to work. To get there, we have to combine this idea with something more. Here’s the trick: instead of sending the canary alone, send a [channel][guide-channels] alongside it. Synchronize on both the canary’s executor *and* the channel so that the thread will unblock if either the canary is collected *or* the channel is received and sent a value using `channel-put`. Have the receiver listen for the channel on a separate thread, and when it receives it, send a value back to unblock the waiting thread as quickly as possible, without needing to rely on a timeout or a particular number of calls to `collect-garbage`. - -Using that idea, we can revise the program: - -```racket -;; check-receivers.rkt -#lang racket - -(define (check-receivers topic) - (define chan (make-channel)) - (define executor (make-will-executor)) - ; limit scope of `canary` so we don’t retain a reference - (let () - (define canary (gensym 'canary)) - (will-register executor canary void) - (log-message (current-logger) 'debug topic "" - ; send the channel + the canary - (vector-immutable chan canary) #f)) - (if (let loop ([n 0]) - (sleep) ; yield to try to let the receiver thread work - (match (sync/timeout 0 - (wrap-evt chan (λ (v) 'received)) - (wrap-evt executor (λ (v) 'collected))) - ['collected #t] - ['received #f] - [_ ; collect garbage and try again - (collect-garbage (if (< n 3) 'minor 'major)) - (loop (add1 n))])) - (printf "no receivers for ~v\n" topic) - (printf "receiver exists for ~v\n" topic))) - -; add a receiver on topic 'foo -(define recv (make-log-receiver (current-logger) 'debug 'foo)) -(void (thread - (λ () - (let loop () - (match (sync recv) - [(vector _ _ (vector chan _) _) - (channel-put chan #t) - (loop)]))))) - -(define t1 (thread (λ () (check-receivers 'foo)))) -(define t2 (thread (λ () (check-receivers 'bar)))) - -(thread-wait t1) -(thread-wait t2) -``` - -Now the program completes almost instantly. For this simple program, the explicit `(sleep)` call is effective enough at yielding that, on my machine, `(check-receivers 'foo)` returns without ever calling `collect-garbage`, and `(check-receivers 'bar)` returns after performing a single minor collection. - -This is extremely close to a bulletproof solution, but there are two remaining subtle issues: - - 1. There is technically a race condition between the `(sync recv)` in the receiver thread and the subsequent `channel-put`, since it’s possible for the canary to be received, discarded, and garbage collected before reaching the call to `channel-put`, which the sending thread would incorrectly interpret as indicating the topic has no receivers. - - To fix that, the receiver thread can send the canary itself back through the channel, which fundamentally has to work, since the value cannot be collected until it has been received by the sending thread, at which point the `sync` has already chosen the channel. - - 2. It is possible for the receiver thread to receive the log message and call `channel-put`, but for the sending thread to somehow die in the meantime (which cannot be protected against in general in Racket, since `thread-kill` immediately and forcibly terminates a thread). If this were to happen, the sending thread would never obtain the value from the channel, blocking the receiving thread indefinitely. - - A solution is to spawn a new thread for each `channel-put` instead of calling it directly from the receiving thread. Conveniently, this both ensures the receiving thread never gets stuck and avoids resource leaks, since the Racket runtime is smart enough to GC a thread blocked on a channel that has no other references and therefore can never be unblocked. - -With those fixes in place, the program is, to the best of my knowledge, bulletproof. It will always correctly determine whether or not a logger has a listener, with no race conditions or reliance upon unspecified behavior of the Racket runtime. It does, however, make a couple of assumptions. - -First, it assumes that the value of `(current-logger)` is shared between the threads. It is true that `(current-logger)` can be changed, and sometimes is, but it’s usually done via `parameterize`, not mutation of the parameter directly. Therefore, this can largely be mitigated by storing the value of `(current-logger)` at module instantiation time. - -Second, it assumes that no other receivers are listening on the same topic. Technically, even using a unique, uninterned key for the topic is insufficient to ensure that no receivers are listening to it, since a receiver can choose to listen to all topics. However, in practice, it is highly unlikely that any receiver would willfully choose to listen to all topics at the `'debug` level, since the receiver would be inundated with enormous amounts of useless information. Even if such a receiver were to be created, it is highly likely that it would dequeue the messages as quickly as possible and discard the accompanying payload, since doing otherwise would cause all messages to be retained in memory, leading to a significant memory leak. - -Both these problems can be mitigated by using a logger other than the root logger, which is easy in this example. However, for the purpose of subverting the separate compilation guarantee, we would have no way to share the logger object itself across phases, defeating the whole purpose, so we are forced to use the root logger and hope the above two assumptions remain true (but they usually do). - -## Preparing the exploit - -If you’ve made it here, congratulations! The most difficult part of this blog post is over. All that’s left is the fun part: performing the exploit. - -The bulk of our implementation is a slightly adapted version of `check-receivers`: - -```racket -;; define-cross-phase.rkt -#lang racket - -(define root-logger (current-logger)) - -(define (make-cross-phase topic thunk) - (define receiver (make-log-receiver root-logger 'debug topic)) - (define chan (make-channel)) - (define executor (make-will-executor)) - - (let () - (define canary (gensym 'canary)) - (will-register executor canary (λ (v) 'collected)) - (log-message root-logger 'debug topic "" - (vector-immutable canary chan) #f) - (let loop () - (match (sync receiver) - [(vector _ _ (vector _ (== chan eq?)) _) - (void)] - [_ - (loop)]))) - - (define execute-evt (wrap-evt executor will-execute)) - (define result (let loop ([n 0]) - (sleep) - (or (sync/timeout 0 chan execute-evt) - (begin - (collect-garbage (if (< n 3) 'minor 'major)) - (loop (add1 n)))))) - - (match result - [(vector _ value) - value] - ['collected - (define value (thunk)) - (thread - (λ () - (let loop () - (match (sync receiver) - [(vector _ _ (vector canary chan) _) - (thread (λ () (channel-put chan (vector-immutable canary value)))) - (loop)])))) - value])) -``` - -There are a few minor differences, which I’ll list: - - 1. The most obvious difference is that `make-cross-phase` does the work of both checking if a receiver exists—which I’ll call the *manager thread*—and spawning it if it doesn’t. If it does end up spawning a manager thread, it evaluates the given thunk to produce a value, which becomes the cross-phase value that will be sent through the channel alongside the canary. - - 2. Once the manager thread is created, subsequent calls to `make-cross-phase` will receive the value through the channel and return it instead of re-invoking `thunk`. This is what ensures the right-hand side of each use of `define/cross-phase` is only ever evaluated once. - - 3. Since `make-cross-phase` needs to create a log receiver if no manager thread exists, it does so immediately, before sending the canary through the logger. This avoids a race condition between multiple threads that are simultaneously competing to become the manager thread, where both threads could send a canary through the logger before either was listening, both canaries would get GC’d, and both threads would spawn a new manager.[^4] - - Creating the receiver before sending the canary avoids this problem, but the thread now needs to receive its own canary and discard it before synchronizing on the channel and executor, since otherwise it will retain a reference to the canary. It’s possible that in between creating the receiver and sending the canary, another thread also sent a canary, so it needs to drop any log messages it finds that don’t include its own canary. - - This ends up working out perfectly, since every thread drops all the messages received before the one containing its own canary, but retains all subsequent values. This means that only one thread can ever “win” and become the manager, since the first thread to send a canary is guaranteed to retain all subsequent canaries, yet also guaranteed its canary will be GC’d. Other threads racing to become the manager will remain blocked until the manager thread is created, since its canaries will be retained by the manager-to-be until it dequeues them. - - (This is the most subtle part of the process to get right, but conveniently, it mostly just works out without very much code. If you didn’t understand any of the above three paragraphs, it isn’t a big deal.) - -The final piece to this puzzle is to define the `define/cross-phase` macro that wraps `make-cross-phase`. The macro is actually slightly more involved than just generating a call to `make-cross-phase` directly, since we’d like to use an uninterned symbol for the topic instead of an interned one, just to ensure it is unique. Ordinarily, this might seem impossible, since an uninterned symbol is fundamentally a unique value that needs to be communicated across phases, and the whole problem we are solving is creating a communication channel that spans phases. However, Racket actually provides some built-in support for sharing uninterned symbols across phases (plus some other kinds of values, but they must always be immutable). To do this, we need to generate a [cross-phase persistent submodule][reference-cross-phase-persistent-modules] that exports an uninterned symbol, then pass that symbol as the topic to `make-cross-phase`: - -```racket -(require (for-syntax racket/syntax) - syntax/parse/define) - -(provide define/cross-phase) - -(define-simple-macro (define/cross-phase x:id e:expr) - #:with topic-mod-name (generate-temporary 'cross-phase-topic-key) - (begin - (module topic-mod-name '#%kernel - (#%declare #:cross-phase-persistent) - (#%provide topic) - (define-values [topic] (gensym "cross-phase"))) - (require 'topic-mod-name) - (define x (make-cross-phase topic (λ () e))))) -``` - -And that’s really it. We’re done. - -## Executing the exploit - -With our implementation of `define/cross-phase` in hand, all that’s left to do is run our original `check-foods.rkt` program and see what happens: - -```sh -$ racket check-food.rkt 'fried chicken' -set-add!: contract violation: -expected: set? -given: (mutable-set "fried chicken" "roasted chicken" "roasted potato" "fried potato") -argument position: 1st -other arguments...: - x: "pineapple" -``` - -Well, I don’t know what you expected. Play stupid games, win stupid prizes. - -This error actually makes sense, but it belies one reason (of many) why this whole endeavor is probably a bad idea. Although we’ve managed to make our mutable set cross-phase persistent, our references to set operations like `set-add!` and `set-member?` are not, and every time `racket/set` is instantiated in a fresh phase, it creates an entirely new instance of the `set` structure type. This means that even though we have a bona fide mutable set, it isn’t actually the type of set that this phase’s `set-add!` understands! - -Of course, this isn’t a problem that some liberal application of `define/cross-phase` can’t solve: - -```racket -;; foods.rkt -#lang racket -(require "define-cross-phase.rkt") -(provide delicious-food? add-delicious-food!) - -(define/cross-phase cross:set-member? set-member?) -(define/cross-phase cross:set-add! set-add!) - -(define/cross-phase delicious-foods (mutable-set)) - -(define (delicious-food? food) - (cross:set-member? delicious-foods food)) - -(define (add-delicious-food! new-food) - (cross:set-add! delicious-foods new-food)) -``` -```sh -$ racket check-food.rkt 'fried chicken' -fried chicken is a delicious food. -$ raco make check-food.rkt -$ racket check-food.rkt 'fried chicken' -fried chicken is not delicious. -``` - -And thus we find that another so-called “guarantee” isn’t. - -# Reflection - -Now comes the time in the blog post when I have to step back and think about what I’ve done. Have mercy. - -Everything in this blog post is a terrible idea. No, you should not use loggers for anything that isn’t logging, you shouldn’t use wills and executors for critical control flow, and obviously you should absolutely not intentionally break one of the most helpful guarantees the Racket module system affords you. - -But I thought it was fun to do all that, anyway. - -The meaningful takeaways from this blog post aren’t that the separate compilation guarantee can be broken, nor that any of the particular techniques I used hold, but that - - 1. ensuring non-trivial guarantees is really hard, - - 2. despite that, the separate compilation guarantee is really, really hard to break, - - 3. the separate compilation guarantee is good, and you should appreciate the luxury it affords you while writing Racket macros, - - 4. avoiding races in a concurrent environment can be extremely subtle, - - 5. and Racket is totally *awesome* for giving me this much rope to hang myself with. - -If you want to hang yourself with Racket, too, [runnable code from this blog post is available here][gist-defeating-separate-compilation]. - -[^1]: This isn’t *strictly* true, since Racket provides sandboxing mechanisms that can compile and execute untrusted code without file system or network access, but this is not the default compilation mode. Usually, it doesn’t matter nearly as much as it might sound: most of the time, if you’re compiling untrusted code, you’re also going to run it, and running untrusted code can do all those things, anyway. - -[^2]: This is actually a *terrible* use case for a macro, since an ordinary function would do just fine, but I’m simplifying a little to keep the example small. - -[^3]: Racket actually provides this functionality directly via the `log-level?` procedure. However, since `log-level?` provides no way to determine how *many* receivers are listening to a topic, using it to guard against creating a receiver is vulnerable to a race condition that the garbage collection-based approach can avoid, as is discussed later. Furthermore, the GC technique is more likely to be resilient to nosy log receivers listening on all topics at the `'debug` level, since they will almost certainly dequeue and discard the value quickly (as otherwise they would leak large quantities of memory). - -[^4]: This race is the one that makes using `log-level?` untenable, since the receiver needs to be created before the topic is checked for listeners to avoid the race, which can’t be done with `log-level?` (since it would always return `#t`). - -[doc-log-levelp]: https://docs.racket-lang.org/reference/logging.html#%28def._%28%28quote._~23~25kernel%29._log-level~3f%29%29 -[gist-defeating-separate-compilation]: https://gist.github.com/lexi-lambda/f173a84fc9727977bcea657b3bb0cd4f -[guide-channels]: https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29 -[guide-general-phase-levels]: https://docs.racket-lang.org/guide/phases.html -[macromod]: https://www.cs.utah.edu/plt/publications/macromod.pdf -[manifesto-pl-pl]: https://felleisen.org/matthias/manifesto/sec_pl-pl.html -[reference-cross-phase-persistent-modules]: https://docs.racket-lang.org/reference/eval-model.html#%28part._cross-phase._persistent-modules%29 -[reference-wills-and-executors]: https://docs.racket-lang.org/reference/willexecutor.html -[separate-compilation-guarantee]: https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29 diff --git a/blog/posts/2019-09-07-demystifying-monadbasecontrol.md b/blog/posts/2019-09-07-demystifying-monadbasecontrol.md deleted file mode 100644 index 678a061..0000000 --- a/blog/posts/2019-09-07-demystifying-monadbasecontrol.md +++ /dev/null @@ -1,816 +0,0 @@ - Title: Demystifying `MonadBaseControl` - Date: 2019-09-07T18:00:00 - Tags: haskell - -[`MonadBaseControl` from the `monad-control` package][monad-control:MonadBaseControl] is a confusing typeclass, and its methods have complicated types. For many people, it’s nothing more than scary, impossible-to-understand magic that is, for some reason, needed when lifting certain kinds of operations. Few resources exist that adequately explain how, why, and when it works, which sadly seems to have resulted in some [FUD](https://en.wikipedia.org/wiki/Fear,_uncertainty,_and_doubt) about its use. - -There’s no doubt that the machinery of `MonadBaseControl` is complex, and the role it plays in practice is often subtle. However, its essence is actually much simpler than it appears, and I promise it can be understood by mere mortals. In this blog post, I hope to provide a complete survey of `MonadBaseControl`—how it works, how it’s designed, and how it can go wrong—in a way that is accessible to anyone with a firm grasp of monads and monad transformers. To start, we’ll motivate `MonadBaseControl` by reinventing it ourselves. - -# The higher-order action problem - -Say we have a function with the following type:[^0] - -```haskell -foo :: IO a -> IO a -``` - -If we have an action built from a transformer stack like - -```haskell -bar :: StateT X IO Y -``` - -then we might wish to apply `foo` to `bar`, but that is ill-typed, since `IO` is not the same as `StateT X IO`. In cases like these, we often use `lift`, but it’s not good enough here: `lift` _adds_ a new monad transformer to an action, but here we need to _remove_ a transformer. So we need a function with a type like this: - -```haskell -unliftState :: StateT X IO Y -> IO Y -``` - -However, if you think about that type just a little bit, it’s clear something’s wrong: it throws away information, namely the state. You may remember that a `StateT X IO Y` action is equivalent to a function of type `X -> IO (Y, X)`, so our hypothetical `unliftState` function has two problems: - - 1. We have no `X` to use as the initial state. - - 2. We’ll lose any modifications `bar` made to the state, since the result type is just `Y`, not `(Y, X)`. - -Clearly, we’ll need something more sophisticated, but what? - -# A naïve solution - -Given that `foo` doesn’t know anything about the state, we can’t easily thread it through `foo` itself. However, by using `runStateT` explicitly, we could do some of the state management ourselves: - -```haskell -foo' :: StateT s IO a -> StateT s IO a -foo' m = do - s <- get - (v, s') <- lift $ foo (runStateT m s) - put s' - pure v -``` - -Do you see what’s going on there? It’s not actually very complicated: we get the current state, then pass it as the initial state to `runStateT`. This produces an action `IO (a, s)` that has *closed over* the current state. We can pass that action to `foo` without issue, since `foo` is polymorphic in the action’s return type. Finally, all we have to do is `put` the modified state back into the enclosing `StateT` computation, and we can get on with our business. - -That strategy works okay when we only have one monad transformer, but it gets hairy quickly as soon as we have two or more. For example, if we had `baz :: ExceptT X (StateT Y IO) Z`, then we *could* do the same trick by getting the underlying - -```haskell -Y -> IO (Either X Z, Y) -``` - -function, closing over the state, restoring it, and doing the appropriate case analysis to re-raise any `ExceptT` errors, but that’s a lot of work to do for every single function! What we’d like to do instead is somehow abstract over the pattern we used to write `foo'` in a way that scales to arbitrary monad transformers. - -# The essence of `MonadBaseControl` - -To build a more general solution for “unlifting” arbitrary monad transformers, we need to start thinking about monad transformer state. The technique we used to implement `foo'` operated on the following process: - - 1. Capture the action’s input state and close over it. - - 2. Package up the action’s output state with its result and run it. - - 3. Restore the action’s output state into the enclosing transformer. - - 4. Return the action’s result. - -For `StateT s`, it turns out that the input state and output state are both `s`, but other monad transformers have state, too. Consider the input and output state for the following common monad transformers: - -
                                            - - - - - - - - - - - - - - - - - - - - - - - - - -
                                            transformerrepresentationinput stateoutput state
                                            StateT s m as -> m (a, s)ss
                                            ReaderT r m ar -> m ar()
                                            WriterT w m am (a, w)()w
                                            -
                                            - -Notice how the input state is whatever is to the left of the `->`, while the output state is whatever extra information gets produced alongside the result. Using the same reasoning, we can also deduce the input and output state for compositions of multiple monad transformers, such as the following: - -
                                            - - - - - - - - - - - - - - - - - - - - - - - - - -
                                            transformerrepresentationinput stateoutput state
                                            ReaderT r (WriterT w m) ar -> m (a, w)rw
                                            StateT s (ReaderT r m) ar -> s -> m (a, s)(r, s)s
                                            WriterT w (StateT s m) as -> m ((a, w), s)s(w, s)
                                            -
                                            - -Notice that when monad transformers are composed, their states are composed, too. This is useful to keep in mind, since our goal is to capture the four steps above in a typeclass, polymorphic in the state of the monad transformers we need to lift through. At minimum, we need two new operations: one to capture the input state and close over it (step 1) and one to restore the output state (step 3). One class we might come up with could look like this: - -```haskell -class MonadBase b m => MonadBaseControl b m | m -> b where - type InputState m - type OutputState m - captureInputState :: m (InputState m) - closeOverInputState :: m a -> InputState m -> b (a, OutputState m) - restoreOutputState :: OutputState m -> m () -``` - -If we can write instances of that typeclass for various transformers, we can use the class’s operations to implement `foo'` in a generic way that works with any combination of them: - -```haskell -foo' :: MonadBaseControl IO m => m a -> m a -foo' m = do - s <- captureInputState - let m' = closeOverInputState m s - (v, s') <- liftBase $ foo m' - restoreOutputState s' - pure v -``` - -So how do we implement those instances? Let’s start with `IO`, since that’s the base case: - -```haskell -instance MonadBaseControl IO IO where - type InputState IO = () - type OutputState IO = () - captureInputState = pure () - closeOverInputState m () = m <&> (, ()) - restoreOutputState () = pure () -``` - -Not very exciting. The `StateT s` instance, on the other hand, is significantly more interesting: - -```haskell -instance MonadBaseControl b m => MonadBaseControl b (StateT s m) where - type InputState (StateT s m) = (s, InputState m) - type OutputState (StateT s m) = (s, OutputState m) - captureInputState = (,) <$> get <*> lift captureInputState - closeOverInputState m (s, ss) = do - ((v, s'), ss') <- closeOverInputState (runStateT m s) ss - pure (v, (s', ss')) - restoreOutputState (s, ss) = lift (restoreOutputState ss) *> put s -``` - -**This instance alone includes most of the key ideas behind `MonadBaseControl`.** There’s a lot going on, so let’s break it down, step by step: - - 1. Start by examining the definitions of `InputState` and `OutputState`. Are they what you expected? You’d be forgiven for expecting the following: - - ```haskell - type InputState (StateT s m) = s - type OutputState (StateT s m) = s - ``` - - After all, that’s what we wrote in the table, isn’t it? - - However, if you give it a try, you’ll find it doesn’t work. `InputState` and `OutputState` must capture the state of the *entire* monad, not just a single transformer layer, so we have to combine the `StateT s` state with the state of the underlying monad. In the simplest case we get - - ```haskell - InputState (StateT s IO) = (s, ()) - ``` - - which is boring, but in a more complex case, we need to get something like this: - - ```haskell - InputState (StateT s (ReaderT IO)) = (s, (r, ())) - ``` - - Therefore, `InputState (StateT s m)` combines `s` with `InputState m` in a tuple, and `OutputState` does the same. - - 2. Moving on, take a look at `captureInputState` and `closeOverInputState`. Just as `InputState` and `OutputState` capture the state of the entire monad, these functions need to be inductive in the same way. - - `captureInputState` acquires the current state using `get`, and it combines it with the remaining monadic state using `lift captureInputState`. `closeOverInputState` uses the captured state to peel off the outermost `StateT` layer, then calls `closeOverInputState` recursively to peel off the rest of them. - - 3. Finally, `restoreOutputState` restores the state of the underlying monad stack, then restores the `StateT` state, ensuring everything ends up back the way it’s supposed to be. - -Take the time to digest all that—work through it yourself if you need to—as it’s a dense piece of code. Once you feel comfortable with it, take a look at the instances for `ReaderT` and `WriterT` as well: - -```haskell -instance MonadBaseControl b m => MonadBaseControl b (ReaderT r m) where - type InputState (ReaderT r m) = (r, InputState m) - type OutputState (ReaderT r m) = OutputState m - captureInputState = (,) <$> ask <*> lift captureInputState - closeOverInputState m (s, ss) = closeOverInputState (runReaderT m s) ss - restoreOutputState ss = lift (restoreOutputState ss) - -instance (MonadBaseControl b m, Monoid w) => MonadBaseControl b (WriterT w m) where - type InputState (WriterT w m) = InputState m - type OutputState (WriterT w m) = (w, OutputState m) - captureInputState = lift captureInputState - closeOverInputState m ss = do - ((v, s'), ss') <- closeOverInputState (runWriterT m) ss - pure (v, (s', ss')) - restoreOutputState (s, ss) = lift (restoreOutputState ss) *> tell s -``` - -Make sure you understand these instances, too. It should be easier this time, since they share most of their structure with the `StateT` instance, but note the asymmetry that arises from the differing input and output states. (It may even help to try and write these instances yourself, focusing on the types whenever you get stuck.) - -If you feel alright with them, then congratulations: you’re already well on your way to grokking `MonadBaseControl`! - -## Hiding the input state - -So far, our implementation of `MonadBaseControl` works, but it’s actually slightly more complicated than it needs to be. As it happens, all valid uses of `MonadBaseControl` will always end up performing the following pattern: - -```haskell -s <- captureInputState -let m' = closeOverInputState m s -``` - -That is, we close over the input state as soon as we capture it. We can therefore combine `captureInputState` and `closeOverInputState` into a single function: - -```haskell -captureAndCloseOverInputState :: m a -> m (b (a, OutputState m)) -``` - -What’s more, we no longer need the `InputState` associated type at all! This is an improvement, since it simplifies the API and removes the possibility for any misuse of the input state, since it’s never directly exposed. On the other hand, it has a more complicated type: it produces a monadic action *that returns another monadic action*. This can be a little more difficult to grok, which is why I presented the original version first, but it may help to consider how the above type arises naturally from the following definition: - -```haskell -captureAndCloseOverInputState m = closeOverInputState m <$> captureInputState -``` - -Let’s update the `MonadBaseControl` class to incorporate this simplification: - -```haskell -class MonadBase b m => MonadBaseControl b m | m -> b where - type OutputState m - captureAndCloseOverInputState :: m a -> m (b (a, OutputState m)) - restoreOutputState :: OutputState m -> m () -``` - -We can then update all the instances to use the simpler API by simply fusing the definitions of `captureInputState` and `closeOverInputState` together: - -```haskell -instance MonadBaseControl IO IO where - type OutputState IO = () - captureAndCloseOverInputState m = pure (m <&> (, ())) - restoreOutputState () = pure () - -instance MonadBaseControl b m => MonadBaseControl b (StateT s m) where - type OutputState (StateT s m) = (s, OutputState m) - captureAndCloseOverInputState m = do - s <- get - m' <- lift $ captureAndCloseOverInputState (runStateT m s) - pure $ do - ((v, s'), ss') <- m' - pure (v, (s', ss')) - restoreOutputState (s, ss) = lift (restoreOutputState ss) *> put s - -instance MonadBaseControl b m => MonadBaseControl b (ReaderT r m) where - type OutputState (ReaderT r m) = OutputState m - captureAndCloseOverInputState m = do - s <- ask - lift $ captureAndCloseOverInputState (runReaderT m s) - restoreOutputState ss = lift (restoreOutputState ss) - -instance (MonadBaseControl b m, Monoid w) => MonadBaseControl b (WriterT w m) where - type OutputState (WriterT w m) = (w, OutputState m) - captureAndCloseOverInputState m = do - m' <- lift $ captureAndCloseOverInputState (runWriterT m) - pure $ do - ((v, s'), ss') <- m' - pure (v, (s', ss')) - restoreOutputState (s, ss) = lift (restoreOutputState ss) *> tell s -``` - -This is already very close to a full `MonadBaseControl` implementation. The `captureAndCloseOverInputState` implementations are getting a little out of hand, but bear with me—they’ll get simpler before this blog post is over. - -## Coping with partiality - -Our `MonadBaseControl` class now works with `StateT`, `ReaderT`, and `WriterT`, but one transformer we haven’t considered is `ExceptT`. Let’s try to extend our table from before with a row for `ExceptT`: - -
                                            - - - - - - - - - - - - - -
                                            transformerrepresentationinput stateoutput state
                                            ExceptT e m am (Either e a)()???
                                            -
                                            - -Hmm… what *is* the output state for `ExceptT`? - -The answer can’t be `e`, since we might not end up with an `e`—the computation might not fail. `Maybe e` would be closer… could that work? - -Well, let’s try it. Let’s write a `MonadBaseControl` instance for `ExceptT`: - -```haskell -instance MonadBaseControl b m => MonadBaseControl b (ExceptT e m) where - type OutputState (ExceptT e m) = (Maybe e, OutputState m) - captureAndCloseOverInputState m = do - m' <- lift $ captureAndCloseOverInputState (runExceptT m) - pure $ do - ((v, s'), ss') <- m' - pure (v, (s', ss')) - restoreOutputState (s, ss) = lift (restoreOutputState ss) *> case s of - Just e -> throwError e - Nothing -> pure () -``` - -Sadly, the above implementation doesn’t typecheck; it is rejected with the following type error: - -``` -• Couldn't match type ‘Either e a’ with ‘(a, Maybe e)’ - Expected type: m (b ((a, Maybe e), OutputState m)) - Actual type: m (b (Either e a, OutputState m)) -• In the second argument of ‘($)’, namely - ‘captureAndCloseOverInputState (runExceptT m)’ - In a stmt of a 'do' block: - m' <- lift $ captureAndCloseOverInputState (runExceptT m) - In the expression: - do m' <- lift $ captureAndCloseOverInputState (runExceptT m) - return do ((v, s'), ss') <- m' - pure (v, (s', ss')) -``` - -We promised a `(a, Maybe e)`, but we have an `Either e a`, and there’s certainly no way to get the former from the latter. Are we stuck? (If you’d like, take a moment to think about how you’d solve this type error before moving on, as it may be helpful for understanding the following solution.) - -The fundamental problem here is *partiality*. The type of the `captureAndCloseOverInputState` method always produces an action in the base monad that includes an `a` *in addition* to some other output state. But `ExceptT` is different: when it an error is raised, it doesn’t produce an `a` at all—it only produces an `e`. Therefore, as written, it’s impossible to give `ExceptT` a `MonadBaseControl` instance. - -Of course, we’d very much *like* to give `ExceptT` a `MonadBaseControl` instance, so that isn’t very satisfying. Somehow, we need to change `captureAndCloseOverInputState` so that it doesn’t always need to produce an `a`. There are a few ways we could accomplish that, but an elegant way to do it is this: - -```haskell -class MonadBase b m => MonadBaseControl b m | m -> b where - type WithOutputState m a - captureAndCloseOverInputState :: m a -> m (b (WithOutputState m a)) - restoreOutputState :: WithOutputState m a -> m a -``` - -We’ve replaced the old `OutputState` associated type with a new `WithOutputState` type, and the key difference between them is that `WithOutputState` describes the type of a *combination* of the result (of type `a`) and the output state, rather than describing the type of the output state alone. For total monad transformers like `StateT`, `ReaderT`, and `WriterT`, `WithOutputState m a` will just be a tuple of the result value and the output state, the same as before. For example, here’s an updated `MonadBaseControl` instance for `StateT`: - -```haskell -instance MonadBaseControl b m => MonadBaseControl b (StateT s m) where - type WithOutputState (StateT s m) a = WithOutputState m (a, s) - captureAndCloseOverInputState m = do - s <- get - lift $ captureAndCloseOverInputState (runStateT m s) - restoreOutputState ss = do - (a, s) <- lift $ restoreOutputState ss - put s - pure a -``` - -Before we consider how this helps us with `ExceptT`, let’s pause for a moment and examine the revised `StateT` instance in detail, as there are some new things going on here: - - - Take a close look at the definition of `WithOutputState (StateT s m) a`. Note that we’ve defined it to be `WithOutputState m (a, s)`, *not* `(WithOutputState m a, s)`. Consider, for a moment, the difference between these types. Can you see why we used the former, not the latter? - - If it’s unclear to you, that’s okay—let’s illustrate the difference with an example. Consider two similar monad transformer stacks: - - ```haskell - m1 :: StateT s (ExceptT e IO) a - m2 :: ExceptT e (StateT s IO) a - ``` - - Both these stacks contain `StateT` and `ExceptT`, but they are layered in a different order. What’s the difference? Well, consider what `m1` and `m2` return once fully unwrapped: - - ```haskell - runExceptT (runStateT m1 s) :: m (Either e (a, s)) - runStateT (runExceptT m2) s :: m (Either e a, s) - ``` - - These results are meaningfully different: in `m1`, the state is discarded if an error is raised, but in `m2`, the final state is always returned, even if the computation is aborted. What does this mean for `WithOutputState`? - - Here’s the important detail: **the state is discarded when `ExceptT` is “inside” `StateT`, not the other way around.** This can be counterintuitive, since the `s` ends up *inside* the `Either` when the `StateT` constructor is on the *outside* and vice versa. This is really just a property of how monad transformers compose, not anything specific to `MonadBaseControl`, so an explanation of why this happens is outside the scope of this blog post, but the relevant insight is that the `m` in `StateT s m a` controls the eventual action’s output state. - - If we had defined `WithOutputState (StateT s m) a` to be `(WithOutputState m a, s)`, we’d be in a pickle, since `m` would be unable to influence the presence of `s` in the output state. Therefore, we have no choice but to use `WithOutputState m (a, s)`. (If you are still confused by this, try it yourself; you’ll find that there’s no way to make the other definition typecheck.) - - - Now that we’ve developed an intuitive understanding of why `WithOutputState` must be defined the way it is, let’s look at things from another perspective. Consider the type of `runStateT` once more: - - ```haskell - runStateT :: StateT s m a -> s -> m (a, s) - ``` - - Note that the result type is `m (a, s)`, with the `m` on the outside. As it happens, this correspondence simplifies the definition of `captureAndCloseOverInputState`, since we no longer have to do any fiddling with its result—it’s already in the proper shape, so we can just return it directly. - - - Finally, this instance illustrates an interesting change to `restoreOutputState`. Since the `a` is now packed inside the `WithOutputState m a` value, the caller of `captureAndCloseOverInputState` needs some way to get the `a` back out! Conveniently, `restoreOutputState` can play that role, both restoring the output state and unpacking the result. - - Even ignoring partial transformers like `ExceptT`, this is an improvement over the old API, as it conveniently prevents the programmer from forgetting to call `restoreOutputState`. However, as we’ll see shortly, it is much more than a convenience: once `ExceptT` comes into play, it is essential! - -With those details addressed, let’s return to `ExceptT`. Using the new interface, writing an instance for `ExceptT` is not only possible, it’s actually rather easy: - -```haskell -instance MonadBaseControl b m => MonadBaseControl b (ExceptT e m) where - type WithOutputState (ExceptT e m) a = WithOutputState m (Either e a) - captureAndCloseOverInputState m = - lift $ captureAndCloseOverInputState (runExceptT m) - restoreOutputState ss = - either throwError pure =<< lift (restoreOutputState ss) -``` - -This instance illustrates why it’s so crucial that `restoreOutputState` have the aforementioned dual role: it must handle the case where no `a` exists at all! In the case of `ExceptT`, it restores the state in the enclosing monad by re-raising an error. - -Now all that’s left to do is update the other instances: - -```haskell -instance MonadBaseControl IO IO where - type WithOutputState IO a = a - captureAndCloseOverInputState = pure - restoreOutputState = pure - -instance MonadBaseControl b m => MonadBaseControl b (ReaderT r m) where - type WithOutputState (ReaderT r m) a = WithOutputState m a - captureAndCloseOverInputState m = do - s <- ask - lift $ captureAndCloseOverInputState (runReaderT m s) - restoreOutputState ss = lift $ restoreOutputState ss - -instance (MonadBaseControl b m, Monoid w) => MonadBaseControl b (WriterT w m) where - type WithOutputState (WriterT w m) a = WithOutputState m (a, w) - captureAndCloseOverInputState m = - lift $ captureAndCloseOverInputState (runWriterT m) - restoreOutputState ss = do - (a, s) <- lift $ restoreOutputState ss - tell s - pure a -``` - -Finally, we can update our lifted variant of `foo` to use the new interface so it will work with transformer stacks that include `ExceptT`: - -```haskell -foo' :: MonadBaseControl IO m => m a -> m a -foo' m = do - m' <- captureAndCloseOverInputState m - restoreOutputState =<< liftBase (foo m') -``` - -At this point, it’s worth considering something: although getting the `MonadBaseControl` class and instances right was a lot of work, the resulting `foo'` implementation is actually incredibly simple. That’s a good sign, since we only have to write the `MonadBaseControl` instances once (in a library), but we have to write functions like `foo'` quite often. - -# Scaling to the real `MonadBaseControl` - -The `MonadBaseControl` class we implemented in the previous section is complete. It is a working, useful class that is equivalent in power to [the “real” `MonadBaseControl` class in the `monad-control` library][monad-control:MonadBaseControl]. However, if you compare the two, you’ll notice that the version in `monad-control` looks a little bit different. What gives? - -Let’s compare the two classes side by side: - -```haskell --- ours -class MonadBase b m => MonadBaseControl b m | m -> b where - type WithOutputState m a - captureAndCloseOverInputState :: m a -> m (b (WithOutputState m a)) - restoreOutputState :: WithOutputState m a -> m a - --- theirs -class MonadBase b m => MonadBaseControl b m | m -> b where - type StM m a - liftBaseWith :: (RunInBase m b -> b a) -> m a - restoreM :: StM m a -> m a -``` - -Let’s start with the similarities, since those are easy: - - - Our `WithOutputState` associated type is precisely equivalent to their `StM` associated type, they just use a (considerably) shorter name. - - - Likewise, our `restoreOutputState` method is precisely equivalent to their `restoreM` method, simply under a different name. - -That leaves `captureAndCloseOverInputState` and `liftBaseWith`. Those two methods both do similar things, but they aren’t identical, and that’s where all the differences lie. To understand `liftBaseWith`, let’s start by inlining the definition of the `RunInBase` type alias so we can see the fully-expanded type: - -```haskell -liftBaseWith - :: MonadBaseControl b m - => ((forall c. m c -> b (StM m c)) -> b a) - -> m a -``` - -That type is complicated! However, if we break it down, hopefully you’ll find it’s not as scary as it first appears. Let’s reimplement the `foo'` example from before using `liftBaseWith` to show how this version of `MonadBaseControl` works: - -```haskell -foo' :: MonadBaseControl IO m => m a -> m a -foo' m = do - s <- liftBaseWith $ \runInBase -> foo (runInBase m) - restoreM s -``` - -This is, in some ways, superficially similar to the version we wrote using our version of `MonadBaseControl`. Just like in our version, we capture the input state, apply `foo` in the `IO` monad, then restore the state. But what exactly is doing the state capturing, and what is `runInBase`? - -Let’s start by adding a type annotation to `runInBase` to help make it a little clearer what’s going on: - -```haskell -foo' :: forall m a. MonadBaseControl IO m => m a -> m a -foo' m = do - s <- liftBaseWith $ \(runInBase :: forall b. m b -> IO (StM m b)) -> - foo (runInBase m) - restoreM s -``` - -That type should look sort of recognizable. If we replace `StM` with `WithOutputState`, then we get a type that looks very similar to that of our original `closeOverInputState` function, except it doesn’t need to take the input state as an argument. How does that work? - -Here’s the trick: `liftBaseWith` starts by capturing the input state, just as before. However, it then builds a function, `runInBase`, which is like `closeOverInputState` partially-applied to the input state it captured. It hands that function to us, and we’re free to apply it to `m`, which produces the `IO (StM m a)` action we need, and we can now pass that action to `foo`. The result is returned in the outer monad, and we restore the state using `restoreM`. - -## Sharing the input state - -At first, this might seem needlessly complicated. When we first started, we separated capturing the input state and closing over it into two separate operations (`captureInputState` and `closeOverInputState`), but we eventually combined them so that we could keep the input state hidden. Why does `monad-control` split them back into two operations again? - -As it turns out, when lifting `foo`, there’s no advantage to the more complicated API of `monad-control`. In fact, we could implement our `captureAndCloseOverInputState` operation in terms of `liftBaseWith`, and we could use that to implement `foo'` the same way we did before: - -```haskell -captureAndCloseOverInputState :: MonadBaseControl b m => m a -> m (b (StM m a)) -captureAndCloseOverInputState m = liftBaseWith $ \runInBase -> pure (runInBase m) - -foo' :: MonadBaseControl IO m => m a -> m a -foo' m = do - m' <- captureAndCloseOverInputState m - restoreM =<< liftBase (foo m') -``` - -However, that approach has a downside once we need to lift more complicated functions. `foo` is exceptionally simple, as it only accepts a single input argument, but what if we wanted to lift a more complicated function that took *two* monadic arguments, such as this one: - -```haskell -bar :: IO a -> IO a -> IO a -``` - -We could implement that by calling `captureAndCloseOverInputState` twice, like this: - -```haskell -bar' :: MonadBaseControl IO m => m a -> m a -> m a -bar' ma mb = do - ma' <- captureAndCloseOverInputState ma - mb' <- captureAndCloseOverInputState mb - restoreM =<< liftBase (bar ma' mb') -``` - -However, that would capture the monadic state twice, which is rather inefficient. By using `liftBaseWith`, the state capturing is done just once, and it’s shared between all calls to `runInBase`: - -```haskell -bar' :: MonadBaseControl IO m => m a -> m a -> m a -bar' ma mb = do - s <- liftBaseWith $ \runInBase -> - bar (runInBase ma) (runInBase mb) - restoreM s -``` - -By providing a “running” function (`runInBase`) instead of direct access to the input state, `liftBaseWith` allows sharing the captured input state between multiple actions without exposing it directly. - -## Sidebar: continuation-passing and impredicativity - -One last point before we move on: although the above explains why `captureAndCloseOverInputState` is insufficient, you may be left wondering why `liftBaseWith` can’t just *return* `runInBase`. Why does it need to be given a continuation? After all, it would be nicer if we could just write this: - -```haskell -bar' :: MonadBaseControl IO m => m a -> m a -> m a -bar' ma mb = do - runInBase <- askRunInBase - restoreM =<< liftBase (bar (runInBase ma) (runInBase mb)) -``` - -To understand the problem with a hypothetical `askRunInBase` function, remember that the type of `runInBase` is polymorphic: - -```haskell -runInBase :: forall a. m a -> b (StM m a) -``` - -This is important, since if you need to lift a function with a type like - -```haskell -baz :: IO b -> IO c -> IO (Either b c) -``` - -then you’ll want to instantiate that `a` variable with two different types. We’d need to retain that power in `askRunInBase`, so it would need to have the following type: - -```haskell -askRunInBase :: MonadBaseControl b m => m (forall a. m a -> b (StM m a)) -``` - -Sadly, that type is illegal in Haskell. Type constructors must be applied to monomorphic types, but in the above type signature, `m` is applied to a polymorphic type.[^1] The `RankNTypes` GHC extension introduces a single exception: the `(->)` type constructor is special and may be applied to polymorphic types. That’s why `liftBaseWith` is legal, but `askRunInBase` is not: since `liftBaseWith` is passed a higher-order function that receives `runInBase` as an argument, the polymorphic type appears immediately under an application of `(->)`, which is allowed. - -The aforementioned restriction means we’re basically out of luck, but if you *really* want `askRunInBase`, there is a workaround. GHC is perfectly alright with a field of a datatype being polymorphic, so we can define a newtype that wraps a suitably-polymorphic function: - -```haskell -newtype RunInBase b m = RunInBase (forall a. m a -> b (StM m a)) -``` - -We can now alter `askRunInBase` to return our newtype, and we can implement it in terms of `liftBaseWith`:[^2] - -```haskell -askRunInBase :: MonadBaseControl b m => m (RunInBase b m) -askRunInBase = liftBaseWith $ \runInBase -> pure $ RunInBase runInBase -``` - -To use `askRunInBase`, we have to pattern match on the `RunInBase` constructor, but it isn’t very noisy, since we can do it directly in a `do` binding. For example, we could implement a lifted version of `baz` this way: - -```haskell -baz' :: MonadBaseControl IO m => m a -> m b -> m (Either a b) -baz' ma mb = do - RunInBase runInBase <- askRunInBase - s <- liftBase (baz (runInBase ma) (runInBase mb)) - bitraverse restoreM restoreM s -``` - -As of version 1.0.2.3, `monad-control` does not provide a newtype like `RunInBase`, so it also doesn’t provide a function like `askRunInBase`. For now, you’ll have to use `liftBaseWith`, but it might be a useful future addition to the library. - -# Pitfalls - -At this point in the blog post, we’ve covered the essentials of `MonadBaseControl`: how it works, how it’s designed, and how you might go about using it. However, so far, we’ve only considered situations where `MonadBaseControl` works well, and I’ve intentionally avoided examples where the technique breaks down. In this section, we’re going to take a look at the pitfalls and drawbacks of `MonadBaseControl`, plus some ways they can be mitigated. - -## No polymorphism, no lifting - -All of the pitfalls of `MonadBaseControl` stem from the same root problem, and that’s the particular technique it uses to save and restore monadic state. We’ll start by considering one of the simplest ways that technique is thwarted, and that’s monomorphism. Consider the following two functions: - -```haskell -poly :: IO a -> IO a -mono :: IO X -> IO X -``` - -Even after all we’ve covered, it may surprise you to learn that although `poly` can be easily lifted to `MonadBaseControl IO m => m a -> m a`, it’s *impossible* to lift `mono` to `MonadBaseControl IO m => m X -> m X`. It’s a little unintuitive, as we often think of polymorphic types as being more complicated (so surely lifting polymorphic functions ought to be harder), but in fact, it’s the flexibility of polymorphism that allows `MonadBaseControl` to work in the first place. - -To understand the problem, remember that when we lift a function of type `forall a. b a -> b a` using `MonadBaseControl`, we actually instantiate `a` to `(StM m c)`. That produces a function of type `b (StM m c) -> b (StM m c)`, which is isomorphic to the `m c -> m c` type we want. The instantiation step is easily overlooked, but it’s crucial, since otherwise we have no way to thread the state through the otherwise opaque function we’re trying to lift! - -In the case of `mono`, that’s exactly the problem we’re faced with. `mono` will not accept an `IO (StM m X)` as an argument, only precisely an `IO X`, so we can’t pass along the monadic state. For all its machinery, `MonadBaseControl` is no help at all if no polymorphism is involved. Trying to generalize `mono` without modifying its implementation is a lost cause. - -## The dangers of discarded state - -Our inability to lift `mono` is frustrating, but at least it’s conclusively impossible. In practice, however, many functions lie in an insidious in-between: polymorphic enough to be lifted, but not without compromises. The simplest of these functions have types such as the following: - -```haskell -sideEffect :: IO a -> IO () -``` - -Unlike `mono`, it’s entirely possible to lift `sideEffect`: - -```haskell -sideEffect' :: MonadBaseControl IO m => m a -> m () -sideEffect' m = liftBaseWith $ \runInBase -> sideEffect (runInBase m) -``` - -This definition typechecks, but you may very well prefer it didn’t, since it has a serious problem: any changes made by `m` to the monadic state are completely discarded once `sideEffect'` returns! Since `sideEffect'` never calls `restoreM`, there’s no way the state of `m` can be any different from the original state, but it’s impossible to call `restoreM` since we don’t actually get an `StM m ()` result from `sideEffect`. - -Sometimes this may be acceptable, since some monad transformers don’t actually have any output state anyway, such as `ReaderT r`. In other cases, however, `sideEffect'` could be a bug waiting to happen. One way to make `sideEffect'` safe would be to add a `StM m a ~ a` constraint to its context, since that guarantees the monad transformers being lifted through are stateless, and nothing is actually being discarded. Of course, that significantly restricts the set of monad transformers that can be lifted through. - -### Rewindable state - -One scenario where state discarding can actually be useful is operations with so-called rewindable or transactional state. The most common example of such an operation is `catch`: - -```haskell -catch :: Exception e => IO a -> (e -> IO a) -> IO a -``` - -When lifted, state changes from the action *or* from the exception handler will be “committed,” but never both. If an exception is raised during the computation, those state changes are discarded (“rewound”), giving `catch` a kind of backtracking semantics. This behavior arises naturally from the way a lifted version of `catch` must be implemented: - -```haskell -catch' :: (Exception e, MonadBaseControl IO m) => m a -> (e -> m a) -> m a -catch' m f = do - s <- liftBaseWith $ \runInBase -> - catch (runInBase m) (runInBase . f) - restoreM s -``` - -If `m` raises an exception, it will never return an `StM m a` value, so there’s no way to get ahold of any of the state changes that happened before the exception. Therefore, the only option is to discard that state. - -This behavior is actually quite useful, and it’s definitely not unreasonable. However, useful or not, it’s inconsistent with state changes to mutable values like `IORef`s or `MVar`s (they stay modified whether an exception is raised or not), so it can still be a gotcha. Either way, it’s worth being aware of. - -### Partially discarded state - -The next function we’re going to examine is `finally`: - -```haskell -finally :: IO a -> IO b -> IO a -``` - -This function has a similar type to `catch`, and it even has similar semantics. Like `catch`, `finally` can be lifted, but unlike `catch`, its state *can’t* be given any satisfying treatment. The only way to implement a lifted version is - -```haskell -finally' :: MonadBaseControl IO m => m a -> m b -> m a -finally' ma mb = do - s <- liftBaseWith $ \runInBase -> - finally (runInBase ma) (runInBase mb) - restoreM s -``` - -which always discards all state changes made by the second argument. This is clear just from looking at `finally`’s type: since `b` doesn’t appear anywhere in the return type, there’s simply no way to access that action’s result, and therefore no way to access its modified state. - -However, don’t despair: there actually *is* a way to produce a lifted version of `finally` that preserves all state changes. It can’t be done by lifting `finally` directly, but if we reimplement `finally` in terms of simpler lifted functions that are more amenable to lifting, we can produce a lifted version of `finally` that preserves all the state:[^3] - -```haskell -finally' :: MonadBaseControl IO m => m a -> m b -> m a -finally' ma mb = mask' $ \restore -> do - a <- liftBaseWith $ \runInBase -> - try (runInBase (restore ma)) - case a of - Left e -> mb *> liftBase (throwIO (e :: SomeException)) - Right s -> restoreM s <* mb -``` - -This illustrates an important (and interesting) point about `MonadBaseControl`: whether or not an operation can be made state-preserving is not a fundamental property of the operation’s type, but rather a property of the types of the exposed primitives. There is sometimes a way to implement a state-preserving variant of operations that might otherwise seem unliftable given the right primitives and a bit of cleverness. - -### Forking state - -As a final example, I want to provide an example where the state may not actually be discarded *per se*, just inaccessible. Consider the type of `forkIO`: - -```haskell -forkIO :: IO () -> IO ThreadId -``` - -Although `forkIO` isn’t actually polymorphic in its argument, we can convert *any* `IO` action to one that produces `()` via `void`, so it might as well be. Therefore, we can lift `forkIO` in much the same way we did with `sideEffect`: - -```haskell -forkIO' :: MonadBaseControl IO m => m () -> m ThreadId -forkIO' m = liftBaseWith $ \runInBase -> forkIO (void $ runInBase m) -``` - -As with `sideEffect`, we can’t recover the output state, but in this case, there’s a fundamental reason that goes deeper than the types: we’ve forked off a concurrent computation! We’ve therefore split the state in two, which might be what we want… but it also might not. `forkIO` is yet another illustration that it’s important to think about the state-preservation semantics when using `MonadBaseControl`, or you may end up with a bug! - -# `MonadBaseControl` in context - -Congratulations: you’ve made it through most of this blog post. If you’ve followed everything so far, you now understand `MonadBaseControl`. All the tricky parts are over. However, before wrapping up, I’d like to add a little extra information about how `MonadBaseControl` relates to various other parts of the Haskell ecosystem. In practice, that information can be as important as understanding `MonadBaseControl` itself. - -## The remainder of `monad-control` - -If you look at [the documentation for `monad-control`][monad-control:Control.Monad.Trans.Control], you’ll find that it provides more than just the `MonadBaseControl` typeclass. I’m not going to cover everything else in detail in this blog post, but I do want to touch upon it briefly. - -First off, you should definitely take a look at the handful of helper functions provided by `monad-control`, such as [`control`](https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:control) and [`liftBaseOp_`](https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:liftBaseOp_). These functions provide support for lifting common function types without having to use `liftBaseWith` directly. It’s useful to understand `liftBaseWith`, since it’s the most general way to use `MonadBaseControl`, but in practice, it is simpler and more readable to use the more specialized functions wherever possible. Many of the examples in this very blog post could be simplified using them, and I only stuck to `liftBaseWith` to introduce as few new concepts at a time as possible. - -Second, I’d like to mention the related [`MonadTransControl`](https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadTransControl) typeclass. You hopefully remember from earlier in the blog post how we defined `MonadBaseControl` instances inductively so that we could lift all the way down to the base monad. `MonadTransControl` is like `MonadBaseControl` if it intentionally did *not* do that—it allows lifting through a single transformer at a time, rather than through all of them at once. - -Usually, `MonadTransControl` is not terribly useful to use directly (though I did use it once [in a previous blog post of mine](/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/#making-mtls-classes-derivable) to help derive instances of mtl-style classes), but it *is* useful for implementing `MonadBaseControl` instances for your own transformers. If you define a `MonadTransControl` instance for your monad transformer, you can get a `MonadBaseControl` implementation for free using the provided [`ComposeSt`](https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:ComposeSt), [`defaultLiftBaseWith`](https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultLiftBaseWith), and [`defaultRestoreM`](https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultRestoreM) bindings; see the documentation for more details. - -## `lifted-base` and `lifted-async` - -If you’re going to use `MonadBaseControl`, the [`lifted-base`][lifted-base] and [`lifted-async`][lifted-async] packages are good to know about. As their names imply, they provide lifted versions of bindings in the `base` and `async` packages, so you can use them directly without needing to lift them yourself. For example, if you needed a lifted version of `mask` from `Control.Exception`, you could swap it for the `mask` export from `Control.Exception.Lifted`, and everything would mostly just work (though always be sure to check the documentation for any caveats on state discarding). - -## Relationship to `MonadUnliftIO` - -Recently, FP Complete has developed the [`unliftio`](https://hackage.haskell.org/package/unliftio) package as an alternative to `monad-control`. It provides the [`MonadUnliftIO`](https://hackage.haskell.org/package/unliftio-core-0.1.2.0/docs/Control-Monad-IO-Unlift.html#t:MonadUnliftIO) typeclass, which is similar in spirit to `MonadBaseControl`, but heavily restricted: it is specialized to `IO` as the base monad, and it *only* allows instances for stateless monads, such as `ReaderT`. This is designed to encourage the so-called [`ReaderT` design pattern](https://www.fpcomplete.com/blog/2017/06/readert-design-pattern), which avoids ever using stateful monads like `ExceptT` or `StateT` over `IO`, encouraging the use of `IO` exceptions and mutable variables (e.g. `MVar`s or `TVar`s) instead. - -I should be clear: I really like most of what FP Complete has done—to this day, I still use `stack` as my Haskell build tool of choice—and I think the suggestions given in the aforementioned “`ReaderT` design pattern” blog post have real weight to them. I have a deep respect for Michael Snoyman’s commitment to opinionated, user-friendly tools and libraries. But truthfully, I can’t stand `MonadUnliftIO`. - -`MonadUnliftIO` is designed to avoid all the complexity around state discarding that `MonadBaseControl` introduces, and on its own, that’s a noble goal. Safety first, after all. The problem is that `MonadUnliftIO` really is extremely limiting, and what’s more, it can actually be trivially encoded in terms of `MonadBaseControl` as follows: - -```haskell -type MonadUnliftIO m = (MonadBaseControl IO m, forall a. StM m a ~ a) -``` - -This alias can be used to define safe, lifted functions that never discard state while still allowing functions that *can* be safely lifted through stateful transformers to do so. Indeed, the [`Control.Concurrent.Async.Lifted.Safe`](https://hackage.haskell.org/package/lifted-async-0.10.0.4/docs/Control-Concurrent-Async-Lifted-Safe.html) module from `lifted-async` does exactly that (albeit with a slightly different formulation than the above alias). - -To be fair, the `unliftio` README does address this in its [comparison section](https://github.com/fpco/unliftio/tree/bb2e26e7fbbaebb15555f417ba9753a76b3218b2/unliftio#monad-control): - -> `monad-control` allows us to unlift both styles. In theory, we could write a variant of `lifted-base` that never does state discards […] In other words, this is an advantage of `monad-control` over `MonadUnliftIO`. We've avoided providing any such extra typeclass in this package though, for two reasons: -> -> - `MonadUnliftIO` is a simple typeclass, easy to explain. We don't want to complicated [sic] matters […] -> -> - Having this kind of split would be confusing in user code, when suddenly [certain operations are] not available to us. - -In other words, the authors of `unliftio` felt that `MonadBaseControl` was simply not worth the complexity, and they could get away with `MonadUnliftIO`. Frankly, if you feel the same way, by all means, use `unliftio`. I just found it too limiting given the way I write Haskell, plain and simple. - -# Recap - -So ends another long blog post. As often seems the case, I set out to write something short, but I ended up writing well over 5,000 words. I suppose that means I learned something from this experience, too: `MonadBaseControl` is more complicated than I had anticipated! Maybe there’s something to take away from that. - -In any case, it’s over now, so I’d like to briefly summarize what we’ve covered: - - - [`MonadBaseControl`][monad-control:MonadBaseControl] allows us to lift higher-order monadic operations. - - - It operates by capturing the current monadic state and explicitly threading it through the action in the base monad before restoring it. - - - That technique works well for polymorphic operations for the type `forall a. b a -> b a`, but it can be tricky or even impossible for more complex operations, sometimes leading to discarded state. - - This can sometimes be mitigated by restricting certain operations to stateless monads using a `StM m a ~ a` constraint, or by reimplementing the operation in terms of simpler primitives. - - - The [`lifted-base`][lifted-base] and [`lifted-async`][lifted-async] packages provide lifted versions of existing operations, avoiding the need to lift them yourself. - -As with many abstractions in Haskell, don’t worry too much if you don’t have a completely firm grasp of `MonadBaseControl` at first. Insight often comes with repeated experience, and `monad-control` can still be used in useful ways even without a perfect understanding. My hope is that this blog post has helped you build intuitions about `MonadBaseControl` even if some of the underlying machinery remains a little fuzzy, and I hope it can also serve as a reference for those who want or need to understand (or just be reminded of) all the little details. - -Finally, I’ll admit `MonadBaseControl` isn’t especially elegant or beautiful as Haskell abstractions go. In fact, in many ways, it’s a bit of a kludge! Perhaps, in time, effect systems will evolve and mature so that it and its ilk are no longer necessary, and they may become distant relics of an inferior past. But in the meantime, it’s here, it’s useful, and I think it’s worth embracing. If you’ve shied away from it in the past, I hope I’ve illuminated it enough to make you consider giving it another try. - -[^0]: One example of a function with that type is `mask_`. - -[^1]: Types with polymorphic types under type constructors are called *impredicative*. GHC technically has limited support for impredicativity via the `ImpredicativeTypes` language extension, but as of GHC 8.8, it has been fairly broken for some time. A fix is apparently being worked on, but even if that effort is successful, I don’t know what impact it will have on type inference. - -[^2]: Note that `askRunInBase = liftBaseWith (pure . RunInBase)` does *not* typecheck, as it would require impredicative polymorphism: it would require instantiating the type of `(.)` with polymorphic types. The version using `($)` works because GHC actually has special typechecking rules for `($)`! Effectively, `f $ x` is really syntax in GHC. - -[^3]: Assume that `mask'` is a suitably lifted version of `mask` (which can in fact be made state-preserving). - - -[lifted-async]: http://hackage.haskell.org/package/lifted-async -[lifted-base]: http://hackage.haskell.org/package/lifted-base -[monad-control:Control.Monad.Trans.Control]: https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html -[monad-control:MonadBaseControl]: https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl diff --git a/blog/posts/2019-10-19-empathy-and-subjective-experience-in-programming-languages.md b/blog/posts/2019-10-19-empathy-and-subjective-experience-in-programming-languages.md deleted file mode 100644 index 27f1ed0..0000000 --- a/blog/posts/2019-10-19-empathy-and-subjective-experience-in-programming-languages.md +++ /dev/null @@ -1,122 +0,0 @@ - Title: Empathy and subjective experience in programming languages - Date: 2019-10-19T08:35:44 - Tags: haskell, programming languages, philosophy - -A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time. - -Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others? - -I think about that question a lot. - -# 2015 called, and they want their dress back - -Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet. - -However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into *Star Wars*. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.[^1] - -There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible. - -Take 2015’s phenomenon of “[the dress][wiki:the-dress]” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did *this*—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality. - -## When something objective isn’t - -Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all. - -The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly. - -In the case of the dress, whether you [heard “yanny” or “laurel,”][wiki:yanny-laurel] or whether you believe the *Sonic* games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm? - -I think the answer is absolutely, unequivocally *yes*. - -# Subjectivity in programming, and in programming languages specifically - -Quick question: which is better, functional or imperative programming? - -My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right? - -Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell? - -I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,[^2] but I did happen to find a link to [a recent discussion][hn:haskell-in-production] on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we? - -In a [branch of the discussion](https://news.ycombinator.com/item?id=21284383), one user writes: - -> > Haskell is great for business and great in production -> -> I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit. -> -> I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust. - -The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response: - -> I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale. -> -> […] -> -> From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on. - -Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues: - -> Haskell gives one plenty of rope to hang himself on complexity. -> -> So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable. - -Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things: - -> Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it. -> -> […] -> -> The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness. -> -> Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs. - -That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see? - -## The unsatisfying subjective reality of programming languages - -You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that? - -***No!*** - -These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, *why?* Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other. - -While [one commenter](https://news.ycombinator.com/item?id=21284317) in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” [another](https://news.ycombinator.com/item?id=21284540) says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds. - -Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t *get it.* There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough. - -## Empathy, and how bad results come from good intentions - -I’ll admit that these kinds of discussions aren’t *always* fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits. - -Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision. - -Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread: - -> Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something. - -I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think? - -If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying. - -Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful. - -To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this: - -> It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction. -> -> Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language. - -When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective **without** invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about *your own* experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements. - -It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong. - -Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be *happier.* - - -[^1]: This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is *blah*,” but you deserve better. So you, uh, get a *meta* snarky footnote instead. - -[^2]: Which, to be entirely fair, may well be as subjective as anything else in this blog post. - -[hn:haskell-in-production]: https://news.ycombinator.com/item?id=21282647 -[wiki:the-dress]: https://en.wikipedia.org/wiki/The_dress -[wiki:yanny-laurel]: https://en.wikipedia.org/wiki/Yanny_or_Laurel diff --git a/blog/posts/2019-11-05-parse-don-t-validate.md b/blog/posts/2019-11-05-parse-don-t-validate.md deleted file mode 100644 index b943fd9..0000000 --- a/blog/posts/2019-11-05-parse-don-t-validate.md +++ /dev/null @@ -1,286 +0,0 @@ - Title: Parse, don’t validate - Date: 2019-11-05T17:09:58 - Tags: functional programming, haskell, types - -Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others. - -However, about a month ago, [I was reflecting on Twitter][twitter:json-parsing] about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long: - -
                                            Parse, don’t validate.
                                            - -# The essence of type-driven design - -Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking. - -## The realm of possibility - -One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature: - -```haskell -foo :: Integer -> Void -``` - -Is it possible to implement `foo`? Trivially, the answer is *no*, as `Void` is a type that contains no values, so it’s impossible for *any* function to produce a value of type `Void`.[^1] That example is pretty boring, but the question gets much more interesting if we choose a more realistic example: - -```haskell -head :: [a] -> a -``` - -This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied: - -```haskell -head :: [a] -> a -head (x:_) = x -``` -``` -warning: [-Wincomplete-patterns] - Pattern match(es) are non-exhaustive - In an equation for ‘head’: Patterns not matched: [] -``` - -This message is helpfully pointing out that our function is *partial*, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is `[]`, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either. - -## Turning partial functions total - -To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the `head` function, and we’ll start with the simplest one. - -### Managing expectations - -As established, `head` is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the `Maybe` type: - -```haskell -head :: [a] -> Maybe a -``` - -This buys us the freedom we need to implement `head`—it allows us to return `Nothing` when we discover we can’t produce a value of type `a` after all: - -```haskell -head :: [a] -> Maybe a -head (x:_) = Just x -head [] = Nothing -``` - -Problem solved, right? For the moment, yes… but this solution has a hidden cost. - -Returning `Maybe` is undoubtably convenient when we’re *implementing* `head`. However, it becomes significantly less convenient when we want to actually use it! Since `head` always has the potential to return `Nothing`, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code: - -```haskell -getConfigurationDirectories :: IO [FilePath] -getConfigurationDirectories = do - configDirsString <- getEnv "CONFIG_DIRS" - let configDirsList = split ',' configDirsString - when (null configDirsList) $ - throwIO $ userError "CONFIG_DIRS cannot be empty" - pure configDirsList - -main :: IO () -main = do - configDirs <- getConfigurationDirectories - case head configDirs of - Just cacheDir -> initializeCache cacheDir - Nothing -> error "should never happen; already checked configDirs is non-empty" -``` - -When `getConfigurationDirectories` retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use `head` in `main` to get the first element of the list, the `Maybe FilePath` result still requires us to handle a `Nothing` case that we know will never happen! This is terribly bad for several reasons: - - 1. First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check? - - 2. Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop. - - 3. Finally, and worst of all, this code is a bug waiting to happen! What if `getConfigurationDirectories` were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update `main`, and suddenly the “impossible” error becomes not only possible, but probable. - -The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically *prove* the `Nothing` case impossible, then a modification to `getConfigurationDirectories` that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug. - -### Paying it forward - -Clearly, our modified version of `head` leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, `head` should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that? - -Let’s look at the original (partial) type signature for `head` again: - -```haskell -head :: [a] -> a -``` - -The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, `[a]`). Instead of weakening the return type, we can *strengthen* the argument type, eliminating the possibility of `head` ever being called on an empty list in the first place. - -To do this, we need a type that represents non-empty lists. Fortunately, the existing `NonEmpty` type from `Data.List.NonEmpty` is exactly that. It has the following definition: - -```haskell -data NonEmpty a = a :| [a] -``` - -Note that `NonEmpty a` is really just a tuple of an `a` and an ordinary, possibly-empty `[a]`. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the `[a]` component is `[]`, the `a` component must always be present. This makes `head` completely trivial to implement:[^2] - -```haskell -head :: NonEmpty a -> a -head (x:|_) = x -``` - -Unlike before, GHC accepts this definition without complaint—this definition is *total*, not partial. We can update our program to use the new implementation: - -```haskell -getConfigurationDirectories :: IO (NonEmpty FilePath) -getConfigurationDirectories = do - configDirsString <- getEnv "CONFIG_DIRS" - let configDirsList = split ',' configDirsString - case nonEmpty configDirsList of - Just nonEmptyConfigDirsList -> pure nonEmptyConfigDirsList - Nothing -> throwIO $ userError "CONFIG_DIRS cannot be empty" - -main :: IO () -main = do - configDirs <- getConfigurationDirectories - initializeCache (head configDirs) -``` - -Note that the redundant check in `main` is now completely gone! Instead, we perform the check exactly once, in `getConfigurationDirectories`. It constructs a `NonEmpty a` from a `[a]` using the `nonEmpty` function from `Data.List.NonEmpty`, which has the following type: - -```haskell -nonEmpty :: [a] -> Maybe (NonEmpty a) -``` - -The `Maybe` is still there, but this time, we handle the `Nothing` case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a `NonEmpty FilePath` value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type `NonEmpty a` as being like a value of type `[a]`, plus a *proof* that the list is non-empty. - -By strengthening the type of the argument to `head` instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section: - - - The code has no redundant checks, so there can’t be any performance overhead. - - - Furthermore, if `getConfigurationDirectories` changes to stop checking that the list is non-empty, its return type must change, too. Consequently, `main` will fail to typecheck, alerting us to the problem before we even run the program! - -What’s more, it’s trivial to recover the old behavior of `head` from the new one by composing `head` with `nonEmpty`: - -```haskell -head' :: [a] -> Maybe a -head' = fmap head . nonEmpty -``` - -Note that the inverse is *not* true: there is no way to obtain the new version of `head` from the old one. All in all, the second approach is superior on all axes. - -## The power of parsing - -You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions: - -```haskell -validateNonEmpty :: [a] -> IO () -validateNonEmpty (_:_) = pure () -validateNonEmpty [] = throwIO $ userError "list cannot be empty" - -parseNonEmpty :: [a] -> IO (NonEmpty a) -parseNonEmpty (x:xs) = pure (x:|xs) -parseNonEmpty [] = throwIO $ userError "list cannot be empty" -``` - -These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: `validateNonEmpty` always returns `()`, the type that contains no information, but `parseNonEmpty` returns `NonEmpty a`, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but `parseNonEmpty` gives the caller access to the information it learned, while `validateNonEmpty` just throws it away. - -These two functions elegantly illustrate two different perspectives on the role of a static type system: `validateNonEmpty` obeys the typechecker well enough, but only `parseNonEmpty` takes full advantage of it. If you see why `parseNonEmpty` is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of `parseNonEmpty`’s name. Is it really *parsing* anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe `parseNonEmpty` is a bona-fide parser (albeit a particularly simple one). - -Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and `parseNonEmpty` is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message. - -Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis: - - - The [aeson][hackage:aeson] library provides a `Parser` type that can be used to parse JSON data into domain types. - - - Likewise, [optparse-applicative][hackage:optparse-applicative] provides a set of parser combinators for parsing command-line arguments. - - - Database libraries like [persistent][hackage:persistent] and [postgresql-simple][hackage:postgresql-simple] have a mechanism for parsing values held in an external data store. - - - The [servant][hackage:servant] ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more. - -The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities. - -One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the `NonEmpty` example above: if the parsing and processing logic go out of sync, the program will fail to even compile. - -## The danger of validation - -Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right? - -Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the [language-theoretic security][langsec] field calls *shotgun parsing*. In the 2016 paper, [The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them][langsec:babel], its authors provide the following definition: - -> Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases. - -They go on to explain the problems inherent to such validation techniques: - -> Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict. - -In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be. - -It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary. - -Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require. - -# Parsing, not validating, in practice - -So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.” - -My advice: focus on the datatypes. - -Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list: - -```haskell -checkNoDuplicateKeys :: (MonadError AppError m, Eq k) => [(k, v)] -> m () -``` - -However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a `Map`. Adjust your function’s type signature to accept a `Map` instead of a list of tuples, and implement it as you normally would. - -Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to `Map`, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of `checkNoDuplicateKeys`: - -```haskell -checkNoDuplicateKeys :: (MonadError AppError m, Eq k) => [(k, v)] -> m (Map k v) -``` - -Now the check *cannot* be omitted, since its result is actually necessary for the program to proceed! - -This hypothetical scenario highlights two simple ideas: - - 1. **Use a data structure that makes illegal states unrepresentable.** Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor. - - 2. **Push the burden of proof upward as far as possible, but no further.** Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before *any* of the data is acted upon.[^3] - - If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow. - -In other words, write functions on the data representation you *wish* you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process! - -Here are a handful of additional points of advice, arranged in no particular order: - - - **Let your datatypes inform your code, don’t let your code control your datatypes.** Avoid the temptation to just stick a `Bool` in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later. - - - **Treat functions that return `m ()` with deep suspicion.** Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way. - - - **Don’t be afraid to parse data in multiple passes.** Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive. - - - **Avoid denormalized representations of data, *especially* if it’s mutable.** Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth. - - - **Keep denormalized representations of data behind abstraction boundaries.** If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync. - - - **Use abstract datatypes to make validators “look like” parsers.** Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract `newtype` with a smart constructor to “fake” a parser from a validator. - -As always, use your best judgement. It probably isn’t worth breaking out [singletons][hackage:singletons] and refactoring your entire application just to get rid of a single `error "impossible"` call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next. - -# Recap, reflection, and related reading - -That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge. - -None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about *process*. My hope is that this is a small step in that direction. - -Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post [Type Safety Back and Forth][type-safety-back-and-forth]. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper [Ghosts of Departed Proofs][ghosts-of-departed-proofs], which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here. - -As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try. - - -[^1]: Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit *any* value. These aren’t “real” values (unlike `null` in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that [Fast and Loose Reasoning is Morally Correct](https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf). - -[^2]: In fact, `Data.List.NonEmpty` already provides a `head` function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. - -[^3]: Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. - -[ghosts-of-departed-proofs]: https://kataskeue.com/gdp.pdf -[hackage:aeson]: https://hackage.haskell.org/package/aeson -[hackage:optparse-applicative]: https://hackage.haskell.org/package/optparse-applicative -[hackage:persistent]: https://hackage.haskell.org/package/persistent -[hackage:postgresql-simple]: https://hackage.haskell.org/package/postgresql-simple -[hackage:servant]: https://hackage.haskell.org/package/servant -[hackage:singletons]: https://hackage.haskell.org/package/singletons -[langsec]: http://langsec.org -[langsec:babel]: http://langsec.org/papers/langsec-cwes-secdev2016.pdf -[twitter:json-parsing]: https://twitter.com/lexi_lambda/status/1182242561655746560 -[type-safety-back-and-forth]: https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html diff --git a/blog/posts/2020-01-19-no-dynamic-type-systems-are-not-inherently-more-open.md b/blog/posts/2020-01-19-no-dynamic-type-systems-are-not-inherently-more-open.md deleted file mode 100644 index 49b6035..0000000 --- a/blog/posts/2020-01-19-no-dynamic-type-systems-are-not-inherently-more-open.md +++ /dev/null @@ -1,275 +0,0 @@ - Title: No, dynamic type systems are not inherently more open - Date: 2020-01-19T00:00:00 - Tags: types, haskell, programming languages, functional programming - -Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large. - -This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are *not* about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much. - -# Two typing fallacies - -I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to [my previous blog post][parse-dont-validate]. Two comments in particular caught my eye, [the first of which was posted on /r/programming](https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/): - -> Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program. -> -> This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver. - -Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time. - -[The second comment was left on Hacker News](https://news.ycombinator.com/item?id=21479933), and it is significantly shorter than the first one: - -> What would be the type signature of, say, Python's `pickle.load()`? - -This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright. - -Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages *can* process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit. - -# You can’t process what you don’t know - -The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true. - -The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or [EDN][edn]. - -As a simple example, a login service might emit an event like this one whenever a new user signs up: - -```json -{ - "event_type": "signup", - "timestamp": "2020-01-19T05:37:09Z", - "data": { - "user": { - "id": 42, - "name": "Alyssa", - "email": "alyssa@example.com" - } - } -} -``` - -Some downstream services might listen for these `signup` events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this: - -```js -const handleEvent = ({ event_type, data }) => { - switch (event_type) { - case 'login': - /* ... */ - break - case 'signup': - sendEmail(data.user.email, `Welcome to Blockchain Emporium, ${data.user.name}!`) - break - } -} -``` - -But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who [parse, not validate][parse-dont-validate], the Haskell code might look something like this, instead: - -```haskell -data Event = Login LoginPayload | Signup SignupPayload -data LoginPayload = LoginPayload { userId :: Int } -data SignupPayload = SignupPayload - { userId :: Int - , userName :: Text - , userEmail :: Text } - -instance FromJSON Event where - parseJSON = withObject "Event" \obj -> do - eventType <- obj .: "event_type" - case eventType of - "login" -> Login <$> (obj .: "data") - "signup" -> Signup <$> (obj .: "signup") - _ -> fail $ "unknown event_type: " <> eventType - -instance FromJSON LoginPayload where { ... } -instance FromJSON SignupPayload where { ... } - -handleEvent :: JSON.Value -> IO () -handleEvent payload = case fromJSON payload of - Success (Login LoginPayload { userId }) -> {- ... -} - Success (Signup SignupPayload { userName, userEmail }) -> - sendEmail userEmail $ "Welcome to Blockchain Emporium, " <> userName <> "!" - Error message -> fail $ "could not parse event: " <> message -``` - -It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The *real* problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the `Event` datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare. - -In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the `switch` and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing. - -Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the `Event` type is that we wrote `handleEvent` that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types: - -```js -const handleEvent = ({ event_type, data }) => { - switch (event_type) { - /* ... */ - default: - throw new Error(`unknown event_type: ${event_type}`) - } -} -``` - -We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too: - -```haskell -handleEvent :: JSON.Value -> IO () -handleEvent payload = case fromJSON payload of - {- ... -} - Error _ -> pure () -``` - -This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we *do* care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it. - -This illustrates an important point: the `Event` type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need. - -This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited: - - - It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the `timestamp` field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work! - - - What’s more, it turns out the Haskell code doesn’t actually *use* the `userId` field inside the `SignupPayload` type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field. - - - Finally, we neatly avoid all the gotchas related to shotgun parsing [mentioned in the previous blog post][parse-dont-validate:shotgun], since we still haven’t compromised on any of those principles. - -We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be. - -The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an `event_type` field, and it assumes `signup` payloads include `data.user.name` and `data.user.email` fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things. - -# Keeping opaque data opaque - -In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim. - -Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way: - -```js -const handleEvent = (payload) => { - const signedPayload = { ...payload, signature: signature(payload) } - retransmitEvent(signedPayload) -} -``` - -In this case, we don’t care about the structure of the payload at all (the `signature` function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type? - -Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell: - -```haskell -handleEvent :: JSON.Value -> IO () -handleEvent (Object payload) = do - let signedPayload = Map.insert "signature" (signature payload) payload - retransmitEvent signedPayload -handleEvent payload = fail $ "event payload was not an object " <> show payload -``` - -In this case, since we don’t care about the structure of the payload, we manipulate a value of type `JSON.Value` directly. This type is extremely imprecise compared to our `Event` type from earlier—it can hold any legal JSON value, of any shape—but in this case, we *want* it to be imprecise. - -Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it. - -Once more, note that the assumption we were forced to make explicit in Haskell is *also* made by the JavaScript code! If our JavaScript `handleEvent` function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise: - -```js -> { ..."payload", signature: "sig" } -{0: "p", 1: "a", 2: "y", 3: "l", 4: "o", 5: "a", 6: "d", signature: "sig"} -``` - -Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the `Object` case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns. - ---- - -Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a `UUID` type: - -```haskell -type UserId = UUID -``` - -However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems? - -Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a `UserId` is to define a new, opaque type: - -```haskell -newtype UserId = UserId Text - deriving (Eq, FromJSON, ToJSON) -``` - -Unlike the type alias defined above which simply creates a new name for the existing `UUID` type, this declaration creates a totally new `UserId` type that is distinct from all other types, including `Text`. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the *only* way to produce a `UserId` will be to go through its `FromJSON` parser. Dually, the only things you can do with a `UserId` are compare it with other `UserId`s for equality or serialize it using the `ToJSON` instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs. - -This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a `UserId` is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new `UserId` out of thin air from an arbitrary string.[^1] - -The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs. - -# Reflection is not special - -We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What *is* the type of Python’s `pickle.load()`? For those unfamiliar, [Python’s cutely-named `pickle` library][python:pickle] allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using `pickle.dump()`, and it can be deserialized at a later point in time using `pickle.load()`. - -What makes this appear challenging to our static type system is that the type of value produced by `pickle.load()` is difficult to predict—it depends entirely on whatever happened to be written to that file using `pickle.dump()`. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t. - -However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens *after* a program calls `pickle.load()`. Say you write the following function: - -```python -def load_value(f): - val = pickle.load(f) - # do something with `val` -``` - -The trouble is that `val` can now be of *any* type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing `pickle.load(f)` returned—and it turns out those assumptions *are* `val`’s type! - -For example, imagine the only thing you do with `val` is call the `val.foo()` method and return its result, which is expected to be a string. If we were writing Java, then the expected type of `val` would be quite straightforward—we’d expect it to be an instance of the following interface: - -```java -interface Foo extends Serializable { - String foo(); -} -``` - -And indeed, it turns out a `pickle.load()`-like function can be given a perfectly reasonable type in Java: - -```java -static Optional load(InputStream in, Class cls); -``` - -Nitpickers will complain that this isn’t the same as `pickle.load()`, since you have to pass a `Class` token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing `Serializable.class` and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do *anything* with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads. - ---- - -Can we do this in Haskell, too? Absolutely—we can use [the `serialise` library][hackage:serialise], which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to [the Haskell JSON library, aeson][hackage:aeson], as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value. - -That said, while you *can* emulate the dynamic typing of `pickle.load()` if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because *you wrote the code*. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front. - -This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors *is* a value’s type. - -Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems *do* impose restrictions on program structure, as it is provably impossible to reject *all* bad programs in a Turing-complete language without also rejecting some good ones (this is [Rice’s theorem][rices-theorem]). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all. - -# Appendix: the reality behind the myths - -The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines. - -However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas. - -Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs). - -These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record. - -In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term *nominal typing*. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate. - -This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws: - - 1. It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but *impossible* to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling. - - 2. It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal. - -For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework. - -If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would *not* recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.[^2] - -Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the *real* reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!) - -As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is *not* productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this. - - -[^1]: Technically, you could abuse the `FromJSON` instance to convert an arbitrary string to a `UserId`, but this would not be as easy as it sounds, since `fromJSON` can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). - -[^2]: I consider this to be Haskell’s most significant flaw at the time of this writing. - - -[parse-dont-validate]: /blog/2019/11/05/parse-don-t-validate/ -[parse-dont-validate:shotgun]: /blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation -[edn]: https://github.com/edn-format/edn -[python:pickle]: https://docs.python.org/3/library/pickle.html -[hackage:serialise]: https://hackage.haskell.org/package/serialise -[hackage:aeson]: https://hackage.haskell.org/package/aeson -[rices-theorem]: https://en.wikipedia.org/wiki/Rice's_theorem diff --git a/blog/posts/2020-08-13-types-as-axioms-or-playing-god-with-static-types.md b/blog/posts/2020-08-13-types-as-axioms-or-playing-god-with-static-types.md deleted file mode 100644 index 870f128..0000000 --- a/blog/posts/2020-08-13-types-as-axioms-or-playing-god-with-static-types.md +++ /dev/null @@ -1,346 +0,0 @@ - Title: Types as axioms, or: playing god with static types - Date: 2020-08-13T13:51:57 - Tags: types, functional programming, haskell, typescript - -Just what exactly *is* a type? - -A common perspective is that types are *restrictions*. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t *really* predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles. - -But that is not the *only* perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves *you.* - -…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective. - -# Seeing the types half-empty - -Let’s talk a little about TypeScript. - -TypeScript is a *gradually-typed* language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to *gradually* add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms. - -Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations[^1] can accept *any* JavaScript value, so adding a type annotation fundamentally restricts the set of legal values. - -Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like `string | number` clearly includes more values than just `number`, so `number` is a more restrictive type—a *subtype*. - -An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers: - -```typescript -function getFirst(arr: number[]): number | undefined { - return arr[0]; -} -``` - -If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write `getFirst(["hello", "world"])`, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument: - -```typescript -function emptyLike(val: number | string): number | string { - if (typeof val === "number") { - return 0; - } else { - return ""; - } -} -``` - -Now if we write `emptyLike(42) * 10`, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back. - -When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away. - -At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of `emptyLike` to `any`. “If it can’t even figure this out, can it *really* be all that useful?” - -Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration: - - * Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors. - - * Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one. - - * Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of `typeof` checks) that obscure the rules the typechecker follows and make them seem semi-magical. - - * Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits. - - * All this frustration breeds a readiness to override the typechecker using casts or `any`, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled. - -The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage. - -# Taking back types - -After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not. - -Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically: - - * Haskell does not have subtyping,[^2] which means that every value belongs to exactly one type. - - * While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.[^3] - - * In particular, Haskell is built around the idea that datatypes can be defined with multiple *cases*, and branching is done via pattern-matching (more on this shortly). - -Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season: - -```haskell -data Season = Spring | Summer | Fall | Winter -``` - -If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named `Season` with four possible values, `Spring`, `Summer`, `Fall`, and `Winter`. - -But what exactly *are* those values? - - * In TypeScript, we’d represent this type with a union of strings, like this: - - ```ts - type Season = "spring" | "summer" | "fall" | "winter"; - ``` - - Here, `Season` is a type that can be one of those four strings, but nothing else. - - * In C, we’d represent this type with an enum, like this: - - ```c - enum season { SPRING, SUMMER, FALL, WINTER }; - ``` - - Here, `SPRING`, `SUMMER`, `FALL`, and `WINTER` are essentially defined to be global aliases for the integers `0`, `1`, `2`, and `3`, and the type `enum season` is essentially an alias for `int`. - -So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply *are*. - -The Haskell declaration invents four completely new constants out of thin air, `Spring`, `Summer`, `Fall`, and `Winter`. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, `Spring` is now a value *distinct from all other values*, even if someone in a different module were to also use the name `Spring`. Haskell type declarations let us play god, creating something from nothing. - -Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and *exactly* one thing: we can branch on them. For example, we can write a function that takes a `Season` as an argument and returns whether or not Christmas occurs during it: - -```haskell -containsChristmas :: Season -> Bool -containsChristmas season = case season of - Spring -> False - Summer -> True -- southern hemisphere - Fall -> False - Winter -> True -- northern hemisphere -``` - -`case` expressions are, to a first approximation, a lot like C-style `switch` statements (though they can do a lot more than this simple example suggests). Using `case`, we can also define conversions from our totally unique `Season` constants to other types, if we want: - -```haskell -seasonToString :: Season -> String -seasonToString season = case season of - Spring -> "spring" - Summer -> "summer" - Fall -> "fall" - Winter -> "winter" -``` - -We can also go the other way around, converting a `String` to a `Season`, but if we try, we run into a problem: what do we return for a string like, say, `"cheesecake"`? In other languages, we might throw an error or return `null`, but Haskell does not have `null`, and errors are generally reserved for truly catastrophic failures. What can we do instead? - -A particularly naïve solution would be to create a type called `MaybeASeason` that has two cases—it can be a valid `Season`, or it can be `NotASeason`: - -```haskell -data MaybeASeason = IsASeason Season | NotASeason - -stringToSeason :: String -> MaybeASeason -stringToSeason seasonString = case seasonString of - "spring" -> IsASeason Spring - "summer" -> IsASeason Summer - "fall" -> IsASeason Fall - "winter" -> IsASeason Winter - _ -> NotASeason -``` - -This shows a feature of Haskell datatypes that C-style enums do *not* have: they aren’t just constants, they can contain other values. A `MaybeASeason` can be one of five different values: `IsASeason Spring`, `IsASeason Summer`, `IsASeason Fall`, `IsASeason Winter`, or `NotASeason`. - -In TypeScript, we’d write `MaybeASeason` more like this: - -```ts -type MaybeASeason = Season | "not-a-season"; -``` - -This is kind of nice, because we don’t have to wrap all our `Season` values with `IsASeason` like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the `IsASeason` wrapper to distinguish the value as a `MaybeASeason` rather than a `Season`. - -Now, you may rightly point out that having to invent a type like `MaybeASeason` every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like `MaybeASeason` that works for *any* underlying type. In Haskell, it looks like this: - -```haskell -data Maybe a = Just a | Nothing -``` - -This defines a generic type, where the `a` in `Maybe a` is a stand-in for some other type, much like the `T` in `Array` in other languages. We can change our `stringToSeason` function to use `Maybe`: - -```haskell -stringToSeason :: String -> Maybe Season -stringToSeason seasonString = case seasonString of - "spring" -> Just Spring - "summer" -> Just Summer - "fall" -> Just Fall - "winter" -> Just Winter - _ -> Nothing -``` - -`Maybe` gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library. - -## Positive versus negative space - -At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data. - -In TypeScript, when we write a type declaration like - -```ts -type Season = "summer" | "spring" | "fall" | "winter"; -``` - -we are defining a type that can be one of those four strings *and nothing else*. All the other strings that *aren’t* one of those four make up `Season`’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air. - -Of course, I suspect you don’t really buy this argument. What makes a string like `"cheesecake"` “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example. - -Suppose you are writing a TypeScript program, and you want a function that only accepts *non-empty* arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there *is* a trick for doing that: - -```ts -type NonEmptyArray = [T, ...T[]]; -``` - -Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this: - -```ts -type EvenArray = T[] satisfies (arr => arr.length % 2 === 0); -``` - -But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.” - -But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:[^4] - -```haskell -data List a = Nil | Cons a (List a) -``` - -This type might be a bit confusing at first if you have not written any Haskell, since it’s *recursive*. All of these are valid values of type `List Int`: - - * `Nil` - * `Cons 1 Nil` - * `Cons 1 (Cons 2 Nil)` - * `Cons 1 (Cons 2 (Cons 3 Nil))` - -The recursive nature of `Cons` is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested `Cons`es we want before we terminate the list with a final `Nil`. - -If we wanted to define an `EvenList` type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict `List` to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the *positive* space of things we want to *include?* - -What do I mean by that? Well, we could define an entirely new type that’s just like `List`, but we make it *impossible* to ever include an odd number of elements: - -```haskell -data EvenList a = EvenNil | EvenCons a a (EvenList a) -``` - -Here are some valid values of type `EvenList Int`: - - * `EvenNil` - * `EvenCons 1 2 EvenNil` - * `EvenCons 1 2 (EvenCons 3 4 EvenNil)` - -Lo and behold, a datatype that can only ever include even numbers of elements! - -Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs: - -```haskell -type EvenList a = List (a, a) -``` - -Now values like `Cons (1, 2) (Cons (3, 4) Nil)` would be valid values of type `EvenList Int`, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even *constructible.* - -**This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”** and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really *are* awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box. - -## Types as axiom schemas - -So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways: - - * Instead of thinking about how to *restrict*, it can be useful to think about how to *correctly construct*. - - * In Haskell, datatype declarations invent new values out of thin air. - - * We can represent a *lot* of different data structures using the incredibly simple framework of “datatypes with several possibilities.” - -Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process. - -In Haskell, when you define a datatype, you’re really defining a new, self-contained set of *axioms* and *inference rules.* That is rather abstract, so let’s make it more concrete. Consider the `List` type again: - -```haskell -data List a = Nil | Cons a (List a) -``` - -Viewed as an axiom schema, this type has one axiom and one inference rule: - - * The empty list is a list. - - * If you have a list, and you add an element to the beginning, the result is also a list. - -The axiom is `Nil`, and the inference rule is `Cons`. Every list[^5] is constructed by starting with the axiom, `Nil`, followed by some number of applications of the inference rule, `Cons`. - -We can take a similar approach when designing the `EvenList` type. The axiom is the same: - - * The empty list is a list with an even number of elements. - -But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time: - - * If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements. - -This corresponds precisely to our `EvenList` declaration: - -```haskell -data EvenList a = EvenNil | EvenCons a a (EvenList a) -``` - -We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule: - - * If you have a list, and you add an element to the beginning, the result is a non-empty list. - -That inference rule corresponds to the following datatype: - -```haskell -data NonEmptyList a = NonEmptyCons a (List a) -``` - -Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers: - - * Zero is a natural number. - * If you have a natural number, its successor (i.e. that number plus one) is also a natural number. - -These are two of the [Peano axioms][wiki:peano], which can be represented in Haskell as the following datatype: - -```haskell -data Natural = Zero | Succ Natural -``` - -Using this type, `Zero` represents 0, `Succ Zero` represents 1, `Succ (Succ Zero)` represents 2, and so on. Just as `EvenList` allowed us to represent any list with an even number of elements but made other values impossible to even express, this `Natural` type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express. - -Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret `Zero` as `0` and `Succ n` as `n + 1`, but that interpretation is not inherent to `Natural`’s definition—it’s all in our heads! We could choose to interpret `Succ n` as `n - 1` instead, in which case we would only be able to represent non-positive integers, or we could interpret `Zero` as `1` and `Succ n` as `n * 2`, in which case we could only represent powers of two. - -I find that people sometimes find this approach troubling, or at least counterintuitive. Is `Succ (Succ Zero)` *really* 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called `number` or `int`, not think to invent a recursive datatype. And admittedly, the `Natural` type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers. - -But in less contrived situations, this approach *is* practical, and in fact it’s highly useful! The quibble that an `EvenList Int` isn’t “really” a `List Int` is rather meaningless, seeing as our definition of `List` was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then. - -So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, **make your datatypes correct by construction**. - -# “But what if I don’t write Haskell?” And other closing thoughts - -I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had *only* written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others. - -I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript *can* do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both `EvenList` and `Natural`: - -```typescript -type EvenList = [] | [T, T, EvenList]; -type Natural = "zero" | { succ: Natural }; -``` - -If anything, **the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”** Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation. - -And in general, *that’s great!* - -Being able to reuse the same data representation is *hugely* beneficial. Functions like `map` and `filter` already exist for ordinary lists/arrays, but a home-grown `EvenList` type needs its own versions. Passing an `EvenList` to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are *obviously* a good thing. - -But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of `any` is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore. - -Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you. - - -[^1]: Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked `any` type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. - -[^2]: Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type `forall a. a -> a` is a subtype of the type `Int -> Int`. But Haskell does not have anything resembling inheritance (e.g. there is no common `Number` supertype that includes both `Int` and `Double`) nor does it have untagged unions (e.g. the argument to a function cannot be something like `Int | String`, you must define a wrapper type like `data IntOrString = AnInt Int | AString String`). - -[^3]: Lists, tuples, and strings do technically have special *syntax*, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. - -[^4]: Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. - -[^5]: Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. - - -[wiki:peano]: https://en.wikipedia.org/wiki/Peano_axioms diff --git a/blog/posts/2020-11-01-names-are-not-type-safety.md b/blog/posts/2020-11-01-names-are-not-type-safety.md deleted file mode 100644 index d847528..0000000 --- a/blog/posts/2020-11-01-names-are-not-type-safety.md +++ /dev/null @@ -1,279 +0,0 @@ - Title: Names are not type safety - Date: 2020-11-01T18:00:00 - Tags: haskell, types, functional programming - -Haskell programmers spend a lot of time talking about *type safety*. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published [Parse, Don’t Validate][blog:pdv] as an initial stab towards bridging that gap. - -The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s `newtype` construct. The idea is simple enough—the `newtype` keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this *sounds* like a simple and straightforward path to type safety. For example, one might consider using a `newtype` declaration to define a type for an email address: - -```haskell -newtype EmailAddress = EmailAddress Text -``` - -This technique can provide *some* value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct *kind* of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name. - -And names are not type safety. - -# Intrinsic and extrinsic safety - -To illustrate the difference between constructive data modeling (discussed at length in my [previous blog post][blog:axioms]) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases: - -```haskell -data OneToFive - = One - | Two - | Three - | Four - | Five -``` - -We could then write some functions to convert between `Int` and our `OneToFive` type: - -```haskell -toOneToFive :: Int -> Maybe OneToFive -toOneToFive 1 = Just One -toOneToFive 2 = Just Two -toOneToFive 3 = Just Three -toOneToFive 4 = Just Four -toOneToFive 5 = Just Five -toOneToFive _ = Nothing - -fromOneToFive :: OneToFive -> Int -fromOneToFive One = 1 -fromOneToFive Two = 2 -fromOneToFive Three = 3 -fromOneToFive Four = 4 -fromOneToFive Five = 5 -``` - -This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead: - -```haskell -newtype OneToFive = OneToFive Int -``` - -Just as before, we can provide `toOneToFive` and `fromOneToFive` functions, with identical types: - -```haskell -toOneToFive :: Int -> Maybe OneToFive -toOneToFive n - | n >= 1 && n <= 5 = Just $ OneToFive n - | otherwise = Nothing - -fromOneToFive :: OneToFive -> Int -fromOneToFive (OneToFive n) = n -``` - -If we put these declarations in their own module and choose not to export the `OneToFive` constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true. - -To see why, suppose we write a function that consumes a `OneToFive` value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive: - -```haskell -ordinal :: OneToFive -> Text -ordinal One = "first" -ordinal Two = "second" -ordinal Three = "third" -ordinal Four = "fourth" -ordinal Five = "fifth" -``` - -The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an `Int`—after all, it *is* an `Int`. An `Int` can of course contain many other values besides `1` through `5`, so we are forced to add an error case to satisfy the exhaustiveness checker: - -```haskell -ordinal :: OneToFive -> Text -ordinal n = case fromOneToFive n of - 1 -> "first" - 2 -> "second" - 3 -> "third" - 4 -> "fourth" - 5 -> "fifth" - _ -> error "impossible: bad OneToFive value" -``` - -In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches: - - * The constructive datatype captures its invariants in such a way that they are *accessible* to downstream consumers. This frees our `ordinal` function from worrying about handling illegal values, as they have been made unutterable. - - * The newtype wrapper provides a smart constructor that *validates* the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting `Int`s. - -Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of `error` has punched a hole right through our type system. If we were to add another constructor to our `OneToFive` datatype,[^1] the version of `ordinal` that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case. - -All of this is a consequence of the fact that the constructive modeling is *intrinsically* type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent `6` using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an `Int`; its meaning is specified extrinsically via the `toOneToFive` smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind. - -## Revisiting non-empty lists - -Our `OneToFive` datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the `NonEmpty` datatype I’ve repeatedly highlighted in recent blog posts: - -```haskell -data NonEmpty a = a :| [a] -``` - -It may be illustrative to imagine a version of `NonEmpty` represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property: - -```haskell -newtype NonEmpty a = NonEmpty [a] - -nonEmpty :: [a] -> Maybe (NonEmpty a) -nonEmpty [] = Nothing -nonEmpty xs = Just $ NonEmpty xs - -instance Foldable NonEmpty where - toList (NonEmpty xs) = xs -``` - -Just as with `OneToFive`, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for `NonEmpty` was the ability to write a safe version of `head`, but the newtype version requires another assertion: - -```haskell -head :: NonEmpty a -> a -head xs = case toList xs of - x:_ -> x - [] -> error "impossible: empty NonEmpty value" -``` - -This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines `NonEmpty`, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof. - -# Newtypes as tokens - -If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes *can* provide a sort of safety, just a weaker one. - -The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a *trust boundary* where internal invariants are enforced by restricting clients to a safe API. - -We can use the `NonEmpty` example from above to illustrate how this works. We refrain from exporting the `NonEmpty` constructor, and we provide `head` and `tail` operations that we trust to never actually fail: - -```haskell -module Data.List.NonEmpty.Newtype - ( NonEmpty - , cons - , nonEmpty - , head - , tail - ) where - -newtype NonEmpty a = NonEmpty [a] - -cons :: a -> [a] -> NonEmpty a -cons x xs = NonEmpty (x:xs) - -nonEmpty :: [a] -> Maybe (NonEmpty a) -nonEmpty [] = Nothing -nonEmpty xs = Just $ NonEmpty xs - -head :: NonEmpty a -> a -head (NonEmpty (x:_)) = x -head (NonEmpty []) = error "impossible: empty NonEmpty value" - -tail :: NonEmpty a -> [a] -tail (NonEmpty (_:xs)) = xs -tail (NonEmpty []) = error "impossible: empty NonEmpty value" -``` - -Since the only way to construct or consume `NonEmpty` values is to use the functions in `Data.List.NonEmpty.Newtype`’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like *tokens*: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case `head` and `tail`, to obtain the values contained within. - -This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid `NonEmpty []` value. For this reason, the newtype approach to type safety does not on its own constitute a *proof* that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.[^2] - -This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a `Generic` instance for `NonEmpty`: - -```haskell -{-# LANGUAGE DeriveGeneric #-} - -import GHC.Generics (Generic) - -newtype NonEmpty a = NonEmpty [a] - deriving (Generic) -``` - -However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary: - -```haskell -ghci> GHC.Generics.to @(NonEmpty ()) (M1 $ M1 $ M1 $ K1 []) -NonEmpty [] -``` - -This is a particularly extreme example, since derived `Generic` instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived `Read` instance: - -```haskell -ghci> read @(NonEmpty ()) "NonEmpty []" -NonEmpty [] -``` - -To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care: - - * All invariants must be made clear to maintainers of the trusted module. For simple types, such as `NonEmpty`, the invariant is self-evident, but for more sophisticated types, comments are not optional. - - * Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants. - - * Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly. - - * Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation. - -In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints. - -In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical. - -# Other newtype use, abuse, and misuse - -The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable: - - * Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the `Sum` and `Product` newtypes from `Data.Monoid` provide useful `Monoid` instances for numeric types. - - * In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The `Flip` newtype from `Data.Bifunctor.Flip` is a simple example, flipping the arguments of a `Bifunctor` so the `Functor` instance may operate on the other side: - - ```haskell - newtype Flip p a b = Flip { runFlip :: p b a } - ``` - - Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas. - - * More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a `ByteString` containing a secret key may be wrapped in a newtype (with a `Show` instance omitted) to discourage code from accidentally logging or otherwise exposing it. - -All of these applications are good ones, but they have little to do with *type safety.* The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually *prevents* misuse; any part of the program may inspect the value at any time. - -Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living: - -```haskell -newtype ArgumentName = ArgumentName { unArgumentName :: GraphQL.Name } - deriving ( Show, Eq, FromJSON, ToJSON, FromJSONKey, ToJSONKey - , Hashable, ToTxt, Lift, Generic, NFData, Cacheable ) -``` - -This newtype is useless noise. Functionally, it is completely interchangeable with its underlying `Name` type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an `ArgumentName`, since the enclosing field name already makes its role clear. - -Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end: - - * Primarily, types distinguish *functional* differences between values. A value of type `NonEmpty a` is *functionally* distinct from a value of type `[a]`, since it is fundamentally structurally different and permits additional operations. In this sense, types are *structural*; they describe what values *are* in the internal world of the programming language. - - * Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate `Distance` and `Duration` types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers. - -Note that both these uses are *pragmatic*; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system *is* a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like `ArgumentName`. - -If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use `Name`, but in situations where the different label adds genuine clarity, one can always use a type alias:[^3] - -```haskell -type ArgumentName = GraphQL.Name -``` - -Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought. - -# Final thoughts and related reading - -I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that. - -Newtypes are one particular mechanism of defining *wrapper types*, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively. - -The catalyst that got me to finally sit down and write this was the recently-published [Tagged is not a Newtype][tagged-is-not-a-newtype]. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, `Tagged` *is* a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper. - -Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name. - -And a name is not type safety. - - -[^1]: Admittedly rather unlikely given its name, but bear with me through the contrived example. - -[^2]: In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. - -[^3]: As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. - - -[blog:axioms]: /blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/ -[blog:pdv]: /blog/2019/11/05/parse-don-t-validate/ -[tagged-is-not-a-newtype]: https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/ diff --git a/blog/posts/2021-03-25-an-introduction-to-typeclass-metaprogramming.md b/blog/posts/2021-03-25-an-introduction-to-typeclass-metaprogramming.md deleted file mode 100644 index 4038640..0000000 --- a/blog/posts/2021-03-25-an-introduction-to-typeclass-metaprogramming.md +++ /dev/null @@ -1,1346 +0,0 @@ - Title: An introduction to typeclass metaprogramming - Date: 2021-03-25T00:00:00 - Tags: haskell, types, functional programming - -*Typeclass metaprogramming* is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the [servant][hackage:servant] ecosystem), and it is the core mechanism used to implement generic programming via [GHC generics][hackage:base:GHC.Generics]. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers. - -This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does *not* attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also *not* a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming. - -# Part 1: Basic building blocks - -Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied. - -To start, we’ll cover the absolute foundations of typeclass metaprogramming. - -## Typeclasses as functions from types to terms - -As its name implies, typeclass metaprogramming (henceforth TMP[^1]) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic `==` operator via the `Eq` class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: **typeclasses are functions from types to (runtime) terms**. - -What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called `TypeOf`: - -```haskell -class TypeOf a where - typeOf :: a -> String -``` - -The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances: - -```haskell -instance TypeOf Bool where - typeOf _ = "Bool" - -instance TypeOf Char where - typeOf _ = "Char" - -instance (TypeOf a, TypeOf b) => TypeOf (a, b) where - typeOf (a, b) = "(" ++ typeOf a ++ ", " ++ typeOf b ++ ")" -``` - -Given these instances, we can observe that they do what we expect in GHCi: - -```haskell -ghci> typeOf (True, 'a') -"(Bool, Char)" -``` - -Note that both the `TypeOf Bool` and `TypeOf Char` instances ignore the argument to `typeOf` altogether. This makes sense, as the whole point of the `TypeOf` class is to get access to *type* information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether: - -```haskell -{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-} - -class TypeOf a where - typeOf :: String -``` - -This typeclass definition is a little unusual, as the type parameter `a` doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition - -```haskell -class Show a where - show :: a -> String -``` - -the full type of the `show` method is implicitly extended with a `Show a` constraint to yield: - -```haskell -show :: Show a => a -> String -``` - -Furthermore, if we write `forall`s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the *full* type of `show`: - -```haskell -show :: forall a. Show a => a -> String -``` - -In the same vein, we can write out the full type of `typeOf`, as given by our new definition of `TypeOf`: - -```haskell -typeOf :: forall a. TypeOf a => String -``` - -This type is still unusual, as the `a` type parameter doesn’t appear anywhere to the right of the `=>` arrow. This makes the type parameter trivially *ambiguous*, which is to say it’s impossible for GHC to infer what `a` should be at any call site. Fortunately, [we can use `TypeApplications`][ghc:type-applications] to pass a type for `a` directly, as we can see in the updated definition of `TypeOf (a, b)`: - -```haskell -instance TypeOf Bool where - typeOf = "Bool" - -instance TypeOf Char where - typeOf = "Char" - -instance (TypeOf a, TypeOf b) => TypeOf (a, b) where - typeOf = "(" ++ typeOf @a ++ ", " ++ typeOf @b ++ ")" -``` - -Once again, we can test out our new definitions in GHCi: - -```haskell -ghci> typeOf @Bool -"Bool" -ghci> typeOf @(Bool, Char) -"(Bool, Char)" -``` - -This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our `typeOf` function is, quite literally, a function that accepts a single type as an argument and returns a term-level `String`. Of course, the `TypeOf` typeclass is not a particularly *useful* example of such a function, but it demonstrates how easy it is to construct. - -## Type-level interpreters - -One important consequence of eliminating the value-level argument of `typeOf` is that there is no need for its argument type to actually be *inhabited*. For example, consider the `TypeOf` instance on `Void` from `Data.Void`: - -```haskell -instance TypeOf Void where - typeOf = "Void" -``` - -This above instance is no different from the ones on `Bool` and `Char` even though `Void` is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of `TypeOf` on lists: - -```haskell -instance TypeOf a => TypeOf [a] where - typeOf = "[" ++ typeOf @a ++ "]" -``` - -If `typeOf` required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type `a` to recursively apply `typeOf` to. But since `typeOf` only accepts a type-level argument, the term-level meaning of the list type poses no obstacle. - -A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions: - -```haskell -data Z -data S a -``` - -It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level: - - * `Z` is a type that represents 0. - - * `S Z` is a type that represents 1. - - * `S (S Z)` is a type that represents 2. - -And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values: - -```haskell -import Numeric.Natural - -class ReifyNat a where - reifyNat :: Natural - -instance ReifyNat Z where - reifyNat = 0 - -instance ReifyNat a => ReifyNat (S a) where - reifyNat = 1 + reifyNat @a -``` - -As its name implies, `reifyNat` reifies a type-level natural number encoded using our datatypes above into a term-level `Natural` value: - -```haskell -ghci> reifyNat @Z -0 -ghci> reifyNat @(S Z) -1 -ghci> reifyNat @(S (S Z)) -2 -``` - -One way to think about `reifyNat` is as an *interpreter* of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation. - -## Overlapping instances - -Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for `Show (Maybe a)`, you aren’t supposed to *also* write an instance for `Show (Maybe Bool)`, since it isn’t clear whether `show (Just True)` should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it. - -Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances. - -As a simple example, suppose we wanted to write a typeclass that checks whether a given type is `()` or not: - -```haskell -class IsUnit a where - isUnit :: Bool -``` - -If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell: - -```haskell --- not actually valid Haskell, just an example -isUnit :: * -> Bool -isUnit () = True -isUnit _ = False -``` - -But if we try to translate this to typeclass instances, we’ll get a problem: - -```haskell -instance IsUnit () where - isUnit = True - -instance IsUnit a where - isUnit = False -``` - -The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.[^2] This means GHC will complain about instance overlap if we try to evaluate `isUnit @()`: - -``` -ghci> isUnit @() - -error: - • Overlapping instances for IsUnit () - arising from a use of ‘isUnit’ - Matching instances: - instance IsUnit a - instance IsUnit () -``` - -To fix this, we have to explicitly mark `IsUnit ()` as overlapping: - -```haskell -instance {-# OVERLAPPING #-} IsUnit () where - isUnit = True -``` - -Now GHC accepts the expression without complaint: - -```haskell -ghci> isUnit @() -True -``` - -What does the `{-# OVERLAPPING #-}` pragma do, exactly? The gory details are [spelled out in the GHC User’s Guide][ghc:overlapping-instances], but the simple explanation is that `{-# OVERLAPPING #-}` relaxes the overlap checker as long as the instance is *strictly more specific* than the instance(s) it overlaps with. In this case, that is true: `IsUnit ()` is trivially more specific than `IsUnit a`, since the former only matches `()` while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like. - -Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition: - -```haskell -guardUnit :: forall a. a -> Either String a -guardUnit x = case isUnit @a of - True -> Left "unit is not allowed" - False -> Right x -``` - -The intent of `guardUnit` is to use `isUnit` to detect if its argument is of type `()`, and if it is, to return an error. However, even though we marked `IsUnit ()` overlapping, we still get an overlapping instance error: - -``` -error: - • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ - Matching instances: - instance IsUnit a - instance [overlapping] IsUnit () - • In the expression: isUnit @a -``` - -What gives? The problem is that GHC simply doesn’t know what type `a` is when compiling `guardUnit`. It *could* be instantiated to `()` where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported. - -This behavior is actually a very, very good thing. If GHC were to blindly pick the `IsUnit a` instance in this case, then `guardUnit` would always take the `False` branch, even when passed a value of type `()`! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when `{-# OVERLAPPING #-}` annotations are used, so it’s important to keep their limitations in mind. - -As it happens, in this particular case, the error is easily remedied. We simply have to add an `IsUnit` constraint to the type signature of `guardUnit`: - -```haskell -guardUnit :: forall a. IsUnit a => a -> Either String a -guardUnit x = case isUnit @a of - True -> Left "unit is not allowed" - False -> Right x -``` - -Now picking the right `IsUnit` instance is deferred to the place where `guardUnit` is used, and the definition is accepted.[^3] - -## Type families are functions from types to types - -In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family: - -```haskell -{-# LANGUAGE TypeFamilies #-} - -type family Sum a b where - Sum Z b = b - Sum (S a) b = S (Sum a b) -``` - -The above is a [closed type family][ghc:closed-type-families], which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of `Sum` would look like this: - -```haskell -data Nat = Z | S Nat - -sum :: Nat -> Nat -> Nat -sum Z b = b -sum (S a) b = S (sum a b) -``` - -As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom. - -To test our definition of `Sum` in GHCi, we can use [the `:kind!` command][ghc:ghci-kind], which prints out a type and its kind after reducing it as much as possible: - -```haskell -ghci> :kind! Sum (S Z) (S (S Z)) -Sum (S Z) (S (S Z)) :: * -= S (S (S Z)) -``` - -We can also combine `Sum` with our `ReifyNat` class from earlier: - -```haskell -ghci> reifyNat @(Sum (S Z) (S (S Z))) -3 -``` - -Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result. - -## Example 1: Generalized `concat` - -Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a `flatten` function similar to like-named functions provided by many dynamically-typed languages. In those languages, `flatten` is like `concat`, but it works on a list of arbitrary depth. For example, we might use it like this: - -```haskell -> flatten [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] -[1, 2, 3, 4, 5, 6, 7, 8] -``` - -In Haskell, lists of different depths have different types, so multiple levels of `concat` have to be applied explicitly. But using TMP, we can write a generic `flatten` function that operates on lists of any depth! - -Since this is *typeclass* metaprogramming, we’ll unsurprisingly begin with a typeclass: - -```haskell -class Flatten a where - flatten :: a -> [???] -``` - -Our first challenge is writing the return type of `flatten`. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that: - -```haskell -type family ElementOf a where - ElementOf [[a]] = ElementOf [a] - ElementOf [a] = a - -class Flatten a where - flatten :: a -> [ElementOf a] -``` - -Now we can write our `Flatten` instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do: - -```haskell -instance Flatten [a] where - flatten x = x -``` - -The inductive case is when the type is a nested list, in which case we want to apply `concat` and recur: - -```haskell -instance {-# OVERLAPPING #-} Flatten [a] => Flatten [[a]] where - flatten x = flatten (concat x) -``` - -Sadly, if we try to compile these definitions, GHC will reject our `Flatten [a]` instance: - -``` -error: - • Couldn't match type ‘a’ with ‘ElementOf [a]’ - ‘a’ is a rigid type variable bound by - the instance declaration - Expected type: [ElementOf [a]] - Actual type: [a] - • In the expression: x - In an equation for ‘flatten’: flatten x = x - In the instance declaration for ‘Flatten [a]’ - | - | flatten x = x - | ^ -``` - -At first blush, this error looks very confusing. Why doesn’t GHC think `a` and `ElementOf [a]` are the same type? Well, consider what would happen if we picked a type like `[Int]` for `a`. Then `[a]` would be `[[Int]]`, a nested list, so the first case of `ElementOf` would apply. Therefore, GHC refuses to pick the second equation of `ElementOf` so hastily. - -In this particular case, we might think that’s rather silly. After all, if `a` were `[Int]`, then GHC wouldn’t have picked the `Flatten [a]` instance to begin with, it would pick the more specific `Flatten [[a]]` instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse. - -Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our `Flatten [a]` instance: - -```haskell -instance (ElementOf [a] ~ a) => Flatten [a] where - flatten x = x -``` - -This is a *type equality constraint*. Type equality constraints are written with the syntax `a ~ b`, and they state that `a` must be the same type as `b`. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that `ElementOf [a]` must always be `a`, which allows the instance to typecheck. - -Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must *eventually* be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the `Flatten [a]` instance is selected, GHC will know that `a` is *not* a list type, and it will be able to reduce `ElementOf [a]` to `a` without difficulty. Indeed, we can see this for ourselves by using `flatten` in GHCi: - -```haskell -ghci> flatten [[[1 :: Integer, 2], [3, 4]], [[5, 6], [7, 8]]] -[1,2,3,4,5,6,7,8] -``` - -It works! But why do we need the type annotation on `1`? If we leave it out, we get a rather hairy type error: - -``` -error: - • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ - Expected type: [ElementOf [a]] - Actual type: [ElementOf [a0]] - NB: ‘ElementOf’ is a non-injective type family - The type variable ‘a0’ is ambiguous -``` - -The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a `Num [a]` instance, in which case `1` could actually have a list type, and either case of `ElementOf` could match depending on the choice of `Num` instance. Of course, no such `Num` instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list. - -This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call `flatten` on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur. - -That wrinkle aside, `flatten` is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime. - -### Typeclasses as compile-time code generation - -Presented with the above definition of `Flatten`, it might not be immediately obvious how to think about `Flatten` as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, `Eq` or `Show`) than the `TypeOf` and `ReifyNat` classes we defined above. - -One useful way to shift our perspective is to consider equivalent `Flatten` instances written using point-free style: - -```haskell -instance (ElementOf [a] ~ a) => Flatten [a] where - flatten = id - -instance {-# OVERLAPPING #-} Flatten [a] => Flatten [[a]] where - flatten = flatten . concat -``` - -These definitions of `flatten` no longer (syntactically) depend on term-level arguments, just like our definitions of `typeOf` and `reifyNat` didn’t accept any term-level arguments above. This allows us to consider what `flatten` might “expand to” given a type argument alone: - - * `flatten @[Int]` is just `id`, since the `Flatten [a]` instance is selected. - - * `flatten @[[Int]]` is `flatten @[Int] . concat`, since the `Flatten [[a]]` instance is selected. That then becomes `id . concat`, which can be further simplified to just `concat`. - - * `flatten @[[[Int]]]` is `flatten @[[Int]] . concat`, which simplifies to `concat . concat` by the same reasoning above. - - * `flatten @[[[[Int]]]]` is then `concat . concat . concat`, and so on. - -This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of `flatten` takes a type as an argument and produces some number of composed `concat`s as a result. From this perspective, `Flatten` is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information. - -This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name *metaprogramming*. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind. - -# Part 2: Generic programming - -Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know. - -In the previous section, we discussed how to use TMP to write a generic `flatten` operation. In this section, we’ll aim a bit higher: totally generic functions that operate on *arbitrary* datatypes. - -## Open type families and associated types - -Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, *open type families*. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using `type instance` declarations. For example, we could define our `Sum` family from above like this: - -```haskell -type family Sum a b -type instance Sum Z b = b -type instance Sum (S a) b = S (Sum a b) -``` - -In the case of `Sum`, this would not be very useful, and indeed, `Sum` is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family. - -This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a `Key` open type family that relates types to the types used to index them: - -```haskell -type family Key a -type instance Key (Vector a) = Int -type instance Key (Map k v) = k -type instance Key (Trie a) = ByteString -``` - -This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key: - -```haskell -class HasKey a where - hasKey :: Key a -> a -> Bool - -instance HasKey (Vector a) where - hasKey i vec = i >= 0 && i < Data.Vector.length vec - -instance HasKey (Map k v) where - hasKey = Data.Map.member - -instance HasKey (Trie a) where - hasKey = Data.Trie.member -``` - -In this case, anyone could define their own data structure, define instances of `Key` and `HasKey` for their data structure, and use `hasKey` to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together: - -```haskell -class HasKey a where - type Key a - hasKey :: Key a -> a -> Bool - -instance HasKey (Vector a) where - type Key (Vector a) = Int - hasKey i vec = i >= 0 && i < Data.Vector.length vec - -instance HasKey (Map k v) where - type Key (Map k v) = k - hasKey = Data.Map.member - -instance HasKey (Trie a) where - type Key (Trie a) = ByteString - hasKey = Data.Trie.member -``` - -An open family declared inside a typeclass like this is called an *associated type*. It works exactly the same way as the separate definitions of `Key` and `HasKey`, it just uses a different syntax. Note that although the `family` and `instance` keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not). - -Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like [`mono-traversable`][hackage:mono-traversable] are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading. - -However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes *heavy* use of open type families: datatype-generic programming. - -## Example 2: Datatype-generic programming - -*Datatype-generic programming* refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include - - * equality, comparison, and hashing, - - * recursive traversal of self-similar data structures, and - - * serialization and deserialization, - -among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on *any* datatype. - -In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The [module documentation for `GHC.Generics`][hackage:base:GHC.Generics] already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP. - -### Generic datatype representations - -At the heart of the `Generic` class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have - -```haskell -data Authentication - = AuthBasic Username Password - | AuthSSH PublicKey -``` - -then we have a type that is essentially equivalent to this one: - -```haskell -type Authentication = Either (Username, Password) PublicKey -``` - -If we know how to define a function on a nested tree built out of `Either`s and pairs, then we know how to define it on *any* such datatype! This is where TMP comes in: recall the way we viewed `Flatten` as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype? - -The answer to that question is *yes*. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, `numFields (AuthBasic "alyssa" "pass1234")` would return `2`, while `numFields (AuthSSH "")` would return `1`. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do. - -We’ll start by using TMP to implement a “generic” version of `numFields` that operates on trees of `Either`s and pairs as described above: - -```haskell -class GNumFields a where - gnumFields :: a -> Natural - --- base case: leaf value -instance GNumFields a where - gnumFields _ = 1 - -instance {-# OVERLAPPING #-} (GNumFields a, GNumFields b) => GNumFields (a, b) where - gnumFields (a, b) = gnumFields a + gnumFields b - -instance {-# OVERLAPPING #-} (GNumFields a, GNumFields b) => GNumFields (Either a b) where - gnumFields (Left a) = gnumFields a - gnumFields (Right b) = gnumFields b -``` - -Just like our `Flatten` class from earlier, `GNumFields` uses the type-level structure of its argument to choose what to do: - - * If we find a pair, that corresponds to a product, so we recur into both sides and sum the results. - - * If we find `Left` or `Right`, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value. - - * In the case of any other value, we’re at a “leaf” in the tree of `Either`s and pairs, which corresponds to a single field, so we just return `1`. - -Now if we call `gnumFields (Left ("alyssa", "pass1234"))`, we’ll get `2`, and if we call `gnumFields (Right "")`, we’ll get `1`. All that’s left to do is write a bit of code that converts our `Authentication` type to a tree of `Either`s and pairs: - -```haskell -genericizeAuthentication :: Authentication -> Either (Username, Password) PublicKey -genericizeAuthentication (AuthBasic user pass) = Left (user, pass) -genericizeAuthentication (AuthSSH key) = Right key - -numFieldsAuthentication :: Authentication -> Natural -numFieldsAuthentication = gnumFields . genericizeAuthentication -``` - -Now we get the results we want on our `Authentication` type using `numFieldsAuthentication`, but we’re not done yet, since it only works on `Authentication` values. Is there a way to define a generic `numFields` function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass: - -```haskell -class Generic a where - type Rep a - genericize :: a -> Rep a - -instance Generic Authentication where - type Rep Authentication = Either (Username, Password) PublicKey - genericize (AuthBasic user pass) = Left (user, pass) - genericize (AuthSSH key) = Right key - -numFields :: (Generic a, GNumFields (Rep a)) => a -> Natural -numFields = gnumFields . genericize -``` - -Now `numFields (AuthBasic "alyssa" "pass1234")` returns `2`, as desired, and it will *also* work with any datatype that provides a `Generic` instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece: - - * First, we define the `Generic` class, comprised of two parts: - - 1. The `Rep a` associated type maps a type `a` onto its generic, sums-of-products representation, i.e. one built out of combinations of `Either` and pairs. - - 2. The `genericize` method converts an actual *value* of type `a` to the equivalent value using the sums-of-products representation. - - * Next, we define a `Generic` instance for `Authentication`. `Rep Authentication` is the sums-of-products representation we described above, and `genericize` is likewise `genericizeAuthentication` from above. - - * Finally, we define `numFields` as a function with a `GNumFields (Rep a)` constraint. This is where all the magic happens: - - * When we apply `numFields` to a datatype, `Rep` retrieves its generic, sums-of-products representation type. - - * The `GNumFields` class then uses various TMP techniques we’ve already described so far in this blog post to generate a `numFields` implementation on the fly from the structure of `Rep a`. - - * Finally, that generated `numFields` implementation is applied to the genericized term-level value, and the result is produced. - -After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) `numFields` operation. Surely just defining `numFields` on each type directly would be far easier? Indeed, if we were just considering `numFields`, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined `numFields`, and all of them would automatically work on `Authentication` because they all leverage the same `Generic` instance! - -This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation *once*, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle. - -### Improving our definition of `Generic` - -You may note that the definition of `Generic` provided above does not match the one in `GHC.Generic`. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a `GHC.Generics` tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP. - -#### Distinguishing leaves from the spine - -One problem with our version of `Generic` is that it provides no way to distinguish an `Either` or pair that should be considered a “leaf”, as in a type like this: - -```haskell -data Foo = A (Either Int String) | B (Char, Bool) -``` - -Given this type, `Rep Foo` should be `Either (Either Int String) (Char, Bool)`, and `numFields (Right ('a', True))` will erroneously return `2` rather than `1`. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically: - -```haskell -newtype Leaf a = Leaf { getLeaf :: a } -``` - -Now our `Generic` instances look like this: - -```haskell -instance Generic Authentication where - type Rep Authentication = Either (Leaf Username, Leaf Password) (Leaf PublicKey) - genericize (AuthBasic user pass) = Left (Leaf user, Leaf pass) - genericize (AuthSSH key) = Right (Leaf key) - -instance Generic Foo where - type Rep Foo = Either (Leaf (Either Int String)) (Leaf (Char, Bool)) - genericize (A x) = Left (Leaf x) - genericize (B x) = Right (Leaf x) -``` - -Since the `Leaf` constructor now distinguishes a leaf, rather than the absence of an `Either` or `(,)` constructor, we’ll have to update our `GNumFields` instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances: - -```haskell -instance GNumFields (Leaf a) where - gnumFields _ = 1 - -instance (GNumFields a, GNumFields b) => GNumFields (a, b) where - gnumFields (a, b) = gnumFields a + gnumFields b - -instance (GNumFields a, GNumFields b) => GNumFields (Either a b) where - gnumFields (Left a) = gnumFields a - gnumFields (Right b) = gnumFields b -``` - -This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable. - -#### Handling empty constructors - -Suppose we have a type with nullary data constructors, like the standard `Bool` type: - -```haskell -data Bool = False | True -``` - -How do we write a `Generic` instance for `Bool`? Using just `Either`, `(,)`, and `Leaf`, we can’t, but if we are willing to add a case for `()`, we can use it to denote nullary constructors: - -```haskell -instance GNumFields () where - gnumFields _ = 0 - -instance Generic Bool where - type Rep Bool = Either () () - genericize False = Left () - genericize True = Right () -``` - -In a similar vein, we could use `Void` to represent datatypes that don’t have any constructors at all. - -### Continuing from here - -The full version of `Generic` has a variety of further improvements useful for generic programming, including: - - * Support for converting from `Rep a` to `a`. - - * Special indication of self-recursive datatypes, making generic tree traversals possible. - - * Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization. - - * Fully automatic generation of `Generic` instances via [the `DeriveGeneric` extension][ghc:derive-generic], which reduces the per-type boilerplate to essentially nothing. - -The [module documentation for `GHC.Generics`][hackage:base:GHC.Generics] discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here. - -# Part 3: Dependent typing - -It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so. - -## Datatype promotion - -In part 1, we used uninhabited datatypes like `Z` and `S a` to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, `True` and `False`: - -```haskell -data True -data False -``` - -Now we could define type families to provide operations on these types, such as `Not`: - -```haskell -type family Not a where - Not True = False - Not False = True -``` - -However, this has some frustrating downsides: - - * First, it’s simply inconvenient that we have to define these new `True` and `False` “dummy” types, which are completely distinct from the `Bool` type provided by the prelude. - - * More significantly, it means `Not` has a very unhelpful kind: - - ```haskell - ghci> :kind Not - Not :: * -> * - ``` - - Even though `Not` is only *supposed* to be applied to `True` or `False`, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like `Not Char`: - - ```haskell - ghci> :kind! Not Char - Not Char :: * - = Not Char - ``` - - Rather than getting an error, GHC simply spits `Not Char` back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process. - -One way to think about `Not` is that it is largely *dynamically kinded* in the same way some languages are dynamically typed. That isn’t entirely true, as we technically *will* get a kind error if we try to apply `Not` to a type constructor rather than a type, such as `Maybe`: - -``` -ghci> :kind! Not Maybe - -:1:5: error: - • Expecting one more argument to ‘Maybe’ - Expected a type, but ‘Maybe’ has kind ‘* -> *’ -``` - -…but `*` is still a very big kind, much bigger than we would like to permit for `Not`. - -To help with both these problems, GHC provides *datatype promotion* via [the `DataKinds` language extension][ghc:data-kinds]. The idea is that for each normal, non-GADT type definition like - -```haskell -data Bool = False | True -``` - -then in addition to the normal type constructor and value constructors, GHC also defines several *promoted* constructors: - - * `Bool` is allowed as both a type and a kind. - - * `'True` and `'False` are defined as new types of kind `Bool`. - -We can see this in action if we remove our `data True` and `data False` declarations and adjust our definition of `Not` to use promoted constructors: - -```haskell -{-# LANGUAGE DataKinds #-} - -type family Not a where - Not 'True = 'False - Not 'False = 'True -``` - -Now the inferred kind of `Not` is no longer `* -> *`: - -```haskell -ghci> :kind Not -Not :: Bool -> Bool -``` - -Consequently, we will now get a kind error if we attempt to apply `Not` to anything other than `'True` or `'False`: - -``` -ghci> :kind! Not Char - -:1:5: error: - • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ -``` - -This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers: - -```haskell -data Nat = Z | S Nat - -class ReifyNat (a :: Nat) where - reifyNat :: Natural - -instance ReifyNat 'Z where - reifyNat = 0 - -instance ReifyNat a => ReifyNat ('S a) where - reifyNat = 1 + reifyNat @a -``` - -Note that we need to add an explicit kind signature on the definition of the `ReifyNat` typeclass, since otherwise GHC will assume `a` has kind `*`, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that `Z` and `S` are related, this prevents someone from coming along and defining a nonsensical instance like `ReifyNat Char`, which previously would have been allowed but will now be rejected with a kind error. - -Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does. - -## GADTs and proof terms - -So far in this blog post, we have discussed several different function-like things: - - * Ordinary Haskell functions are functions from terms to terms. - - * Type families are functions from types to types. - - * Typeclasses are functions from types to terms. - -A curious reader may wonder about the existence of a fourth class of function: - - * *???* are functions from terms to types. - -To reason about what could go in the *???* above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future? - -In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs. - -GADTs[^4] are [described in detail in the GHC User’s Guide][ghc:gadts], but the key idea for our purposes is that *pattern-matching on a GADT constructor can refine type information*. Here’s a simple, silly example: - -```haskell -data WhatIsIt a where - ABool :: WhatIsIt Bool - AnInt :: WhatIsIt Int - -doSomething :: WhatIsIt a -> a -> a -doSomething ABool x = not x -doSomething AnInt x = x + 1 -``` - -Here, `WhatIsIt` is a datatype with two nullary constructors, `ABool` and `AnInt`, similar to a normal, non-GADT datatype like this one: - -```haskell -data WhatIsIt a = ABool | AnInt -``` - -What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, `ABool` and `AnInt` would both have the type `forall a. WhatIsIt a`, but in the GADT definition, we explicitly fix `a` to `Bool` in the type of `ABool` and to `Int` in the type of `AnInt`. - -This simple feature allows us to do very interesting things. The `doSomething` function is polymorphic in `a`, but on the right-hand side of the first equation, `x` has type `Bool`, while on the right-hand side of the second equation, `x` has type `Int`. This is because the `WhatIsIt a` argument effectively constrains the type of `a`, as we can see by experimenting with `doSomething` in GHCi: - -``` -ghci> doSomething ABool True -False -ghci> doSomething AnInt 10 -11 -ghci> doSomething AnInt True - -error: - • Couldn't match expected type ‘Int’ with actual type ‘Bool’ - • In the second argument of ‘doSomething’, namely ‘True’ - In the expression: doSomething AnInt True - In an equation for ‘it’: it = doSomething AnInt True -``` - -One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The `ABool` constructor is a proof of `a ~ Bool`, while the `AnInt` constructor is a proof of `a ~ Int`. When you construct `ABool` or `AnInt`, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match. - -GADTs can be much more sophisticated than our simple `WhatIsIt` type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them: - -```haskell -infixr 5 `HCons` - -data HList as where - HNil :: HList '[] - HCons :: a -> HList as -> HList (a ': as) -``` - -This type is a *heterogenous list*, a list that can contain elements of different types: - -```haskell -ghci> :t True `HCons` "hello" `HCons` 42 `HCons` HNil -True `HCons` "hello" `HCons` 42 `HCons` HNil - :: Num a => HList '[Bool, [Char], a] -``` - -An `HList` is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total `head` function on `HList`s like this: - -```haskell -head :: HList (a ': as) -> a -head (x `HCons` _) = x -``` - -Remarkably, GHC does not complain that this definition of `head` is non-exhaustive. Since we specified that the argument must be of type `HList (a ': as)` in the type signature for `head`, GHC knows that the argument *cannot* be `HNil` (which would have the type `HList '[]`), so it doesn’t ask us to handle that case. - -These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities. - -### Proofs that work together - -This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an `HList` of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with `head`, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case. - -However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a *proof term* that contains no values, it just encapsulates type equalities on a type-level list: - -```haskell -data OneToThree a b c as where - One :: OneToThree a b c '[a] - Two :: OneToThree a b c '[a, b] - Three :: OneToThree a b c '[a, b, c] -``` - -We call it a proof term because a value of type `OneToThree a b c as` constitutes a *proof* that `as` has exactly 1, 2, or 3 elements. Using `OneToThree`, we can write a function that accepts an `HList` accompanied by a proof term: - -```haskell -sumUpToThree :: OneToThree Int Int Int as -> HList as -> Int -sumUpToThree One (x `HCons` HNil) = x -sumUpToThree Two (x `HCons` y `HCons` HNil) = x + y -sumUpToThree Three (x `HCons` y `HCons` z `HCons` HNil) = x + y + z -``` - -As with `head`, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs: - - * When we match on the `OneToThree` proof term, information flows from the term level to the type level, refining the type of `as` in that branch. - - * The refined type of `as` then flows back down to the term level, restricting the shape the `HList` can take and refinine the set of patterns we have to match. - -Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an `HList` has an even number of elements: - -```haskell -data Even as where - EvenNil :: Even '[] - EvenCons :: Even as -> Even (a ': b ': as) -``` - -This is a proof which itself has inductive structure: `EvenCons` takes a proof that `as` has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an `HList`: - -```haskell -type family PairUp as where - PairUp '[] = '[] - PairUp (a ': b ': as) = (a, b) ': PairUp as - -pairUp :: Even as -> HList as -> HList (PairUp as) -pairUp EvenNil HNil = HNil -pairUp (EvenCons even) (x `HCons` y `HCons` xs) = (x, y) `HCons` pairUp even xs -``` - -Once again, this definition is completely exhaustive, and we can show that it works in GHCi: - -```haskell -ghci> pairUp (EvenCons $ EvenCons EvenNil) - (True `HCons` 'a' `HCons` () `HCons` "foo" `HCons` HNil) -(True,'a') `HCons` ((),"foo") `HCons` HNil -``` - -This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful. - -### Proof inference - -While our definition of `pairUp` is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the `Even` proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in. - -Remember that typeclasses are functions from types to terms. As its happens, a value of type `Even as` can be mechanically produced from the structure of the type `as`. This suggests that we could use TMP to automatically generate `Even` proofs, and indeed, we can. In fact, it’s not at all complicated: - -```haskell -class IsEven as where - evenProof :: Even as - -instance IsEven '[] where - evenProof = EvenNil - -instance IsEven as => IsEven (a ': b ': as) where - evenProof = EvenCons evenProof -``` - -We can now adjust our `pairUp` function to use `IsEven` instead of an explicit `Even` argument: - -```haskell -pairUp :: IsEven as => HList as -> HList (PairUp as) -pairUp = go evenProof where - go :: Even as -> HList as -> HList (PairUp as) - go EvenNil HNil = HNil - go (EvenCons even) (x `HCons` y `HCons` xs) = (x, y) `HCons` go even xs -``` - -This is essentially identical to its old definition, but by acquiring the proof via `IsEven` rather than passing it explicitly, we can call `pairUp` without having to construct a proof manually: - -```haskell -ghci> pairUp (True `HCons` 'a' `HCons` () `HCons` "foo" `HCons` HNil) -(True,'a') `HCons` ((),"foo") `HCons` HNil -``` - -This is rather remarkable. Using TMP, we are able to get GHC to *automatically construct a proof that a list is even*, with no programmer guidance beyond writing the `IsEven` typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: `IsEven` is a function that accepts a type-level list and generates an `Even` proof term. - -From this perspective, **typeclasses are a way of specifying a proof search algorithm** to the compiler. In the case of `IsEven`, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system. - -## Aside: GADTs versus type families - -Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things. - -Consider again the type of the `pairUp` function above (without the typeclass for simplicity): - -```haskell -pairUp :: Even as -> HList as -> HList (PairUp as) -``` - -We used both a GADT, `Even`, and a type family, `PairUp`. But we could have, in theory, used *only* a GADT and eliminated the type family altogether. Consider this variation on the `Even` proof term: - -```haskell -data EvenPairs as bs where - EvenNil :: EvenPairs '[] '[] - EvenCons :: EvenPairs as bs -> EvenPairs (a ': b ': as) ((a, b) ': bs) -``` - -This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of `as` as an “input” parameter and `bs` as an “output” parameter. The idea is that any `EvenPairs` proof relates both an even-length list type and its paired up equivalent: - - * `EvenNil` has type `EvenPairs '[] '[]`, - - * `EvenCons EvenNil` has type `EvenPairs '[a, b] '[(a, b)]`, - - * `EvenCons (EvenCons EvenNil)` has type `EvenPairs '[a, b, c, d] '[(a, b), (c, d)]`, - - * …and so on. - -This allows us to reformulate our `pairUp` type signature this way: - -```haskell -pairUp :: EvenPairs as bs -> HList as -> HList bs -``` - -The definition is otherwise unchanged. The `PairUp` type family is completely gone, because now `EvenPairs` itself defines the relation. In this way, GADTs can be used like type-level functions! - -The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type: - -```haskell -import Data.Kind (Constraint) - -type family IsEvenTF as :: Constraint where - IsEvenTF '[] = () - IsEvenTF (_ ': _ ': as) = IsEvenTF as -``` - -The idea here is that `IsEvenTF as` produces a constraint can only be satisfied if `as` has an even number of elements, since that’s the only way it will eventually reduce to `()`, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting `IsEvenTF as =>` in a type signature successfully restricts `as` to be an even-length list, but it doesn’t allow us to write `pairUp`. To see why, we can try the following definition: - -```haskell -pairUp :: IsEvenTF as => HList as -> HList (PairUp as) -pairUp HNil = HNil -pairUp (x `HCons` y `HCons` xs) = (x, y) `HCons` pairUp xs -``` - -Unlike the version using the GADT, this version of `pairUp` is not considered exhaustive: - -``` -warning: [-Wincomplete-patterns] - Pattern match(es) are non-exhaustive - In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil -``` - -This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by `IsEvenTF` provides no term-level evidence about the shape of `as`, so we can’t branch on it the way we can branch on the `Even` GADT.[^5] (In a sense, `IsEvenTF` is doing [validation, not parsing][blog:pdv].) - -For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you. - -## Guiding type inference - -So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed. - -However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible. - -To illustrate what that can look like, suppose we want to use TMP to generate an `HList` full of `()` values of an arbitrary length: - -```haskell -class UnitList as where - unitList :: HList as - -instance UnitList '[] where - unitList = HNil - -instance UnitList as => UnitList (() ': as) where - unitList = () `HCons` unitList -``` - -Testing in GHCi, we can see it behaves as desired: - -```haskell -ghci> unitList :: HList '[(), (), ()] -() `HCons` () `HCons` () `HCons` HNil -``` - -Now suppose we write a function that accepts a list containing exactly one element and returns it: - -```haskell -unsingleton :: HList '[a] -> a -unsingleton (x `HCons` HNil) = x -``` - -Naturally, we would expect these to compose without a hitch. If we write `unsingleton unitList`, our TMP should generate a list of length 1, and we should get back `()`. However, it may surprise you to learn that *isn’t*, in fact, what happens:[^6] - -``` -ghci> unsingleton unitList - -error: - • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ - prevents the constraint ‘(UnitList '[a0])’ from being solved. - Probable fix: use a type annotation to specify what ‘a0’ should be. - These potential instances exist: - instance UnitList as => UnitList (() : as) -``` - -What went wrong? The type error says that `a0` is ambiguous, but it only lists a single matching `UnitList` instance—the one we want—so how can it be ambiguous which one to select? - -The problem stems from the way we defined `UnitList`. When we wrote the instance - -```haskell -instance UnitList as => UnitList (() ': as) where -``` - -we said the first element of the type-level list must be `()`, so there’s nothing stopping someone from coming along and defining another instance: - -```haskell -instance UnitList as => UnitList (Int ': as) where - unitList = 0 `HCons` unitList -``` - -In that case, GHC would have no way to know which instance to pick. Nothing in the type of `unsingleton` forces the element in the list to have type `()`, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start. - -Of course, this isn’t what we want. The `UnitList` class is supposed to *always* return a list of `()` values, so how can we force GHC to pick our instance anyway? The answer is to play a trick: - -```haskell -instance (a ~ (), UnitList as) => UnitList (a ': as) where - unitList = () `HCons` unitList -``` - -Here we’ve changed the instance so that it has the shape `UnitList (a ': as)`, with a type variable in place of the `()`, but we also added an equality constraint that forces `a` to be `()`. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks: - -```haskell -ghci> unsingleton unitList -() -``` - -To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape: - -```haskell -instance => C -``` - -The part to the left of the `=>` is known as the *instance context*, while the part to the right is known as the *instance head*. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, **only the instance head matters, and the instance context is completely ignored**. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context. - -This explains why our two `UnitList` instances behave differently: - - * Given the instance head `UnitList (() ': as)`, GHC won’t select the instance unless it knows the first element of the list is `()`. - - * But given the instance head `UnitList (a ': as)`, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long. - -After the `UnitList (a ': as)` instance is selected, GHC attempts to solve the constraints in the instance context, including the `a ~ ()` constraint. This *forces* `a` to be `()`, resolving the ambiguity and allowing type inference to proceed. - -This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process: - - * If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick. - - * But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell *you* what type this is,” effectively giving you a role in type inference itself. - -From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker. - -Given all of the above, consider again the definition of `IsEven` from earlier: - -```haskell -class IsEven as where - evenProof :: Even as - -instance IsEven '[] where - evenProof = EvenNil - -instance IsEven as => IsEven (a ': b ': as) where - evenProof = EvenCons evenProof -``` - -Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an `IsEven (a ': b0)` constraint, where `b0` is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an `IsEven '[a]` instance in the future. - -To fix this, we can apply the same trick we used for `UnitList`, just in a slightly different way: - -```haskell -instance (as ~ (b ': bs), IsEven bs) => IsEven (a ': as) where - evenProof = EvenCons evenProof -``` - -Again, the idea is to move the type information we *learn* from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can **dramatically** improve type inference in programs that make heavy use of TMP. - -## Example 3: Subtyping constraints - -At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at [Hasura][hasura], I had the opportunity to design an internal parser combinator library that captures aspects of the [GraphQL][graphql] type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both. - -Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our `GQLKind` datatype has three cases: - -```haskell -data GQLKind - = Both - | Input - | Output -``` - -We use `DataKind`-promoted versions of this `GQLKind` type as a parameter to a `GQLType` GADT: - -```haskell -data GQLType k where - TScalar :: GQLType 'Both - TInputObject :: InputObjectInfo -> GQLType 'Input - TIObject :: ObjectInfo -> GQLType 'Output - -- ...and so on... -``` - -This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type `GQLType 'Input`, we can’t pass a `GQLType 'Both`, even though we really ought to be able to. - -To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship: - -```haskell -data SubKind k1 k2 where - KRefl :: SubKind k k - KBoth :: SubKind 'Both k -``` - -The first case, `KRefl`, states that every kind is trivially a subkind of itself. The second case, `KBoth`, states that `Both` is a subkind of any kind at all. (This is a particularly literal example of [using a type to define axioms][blog:axioms].) The next step is to use TMP to implement proof inference: - -```haskell -class IsSubKind k1 k2 where - subKindProof :: SubKind k1 k2 - -instance IsSubKind 'Both k where - subKindProof = KBoth - -instance (k ~ 'Input) => IsSubKind 'Input k where - subKindProof = KRefl - -instance (k ~ 'Output) => IsSubKind 'Output k where - subKindProof = KRefl -``` - -These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that `k` is a superkind of `'Input` or `'Output`, type inference will force them to be equal. - -Using `IsSubKind`, we can easily resolve the problem described above. Rather than write a function with a type like this: - -```haskell -nullable :: GQLParser 'Input a -> GQLParser 'Input (Maybe a) -``` - -…we simply use an `IsSubKind` constraint, instead: - -```haskell -nullable :: IsSubKind k 'Input => GQLParser k a -> GQLParser k (Maybe a) -``` - -Now both `'Input` and `'Both` kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. *Consuming* the `SubKind` proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this: - -```haskell -type family ParserInput k where - ParserInput 'Both = InputValue - ParserInput 'Input = InputValue - ParserInput 'Output = SelectionSet -``` - -This type family is used to determine what a `GQLParser k a` actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that `IsSubKind k 'Input` implies `ParserInput k ~ InputValue`. - -Fortunately, that is very easy to do using [the `(:~:)` type from `Data.Type.Equality` in `base`][hackage:base:Data.Type.Equality] to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition: - -```haskell -data a :~: b where - Refl :: a :~: a -``` - -Just as with any other GADT, `(:~:)` can be used to pack up type equalities and unpack them later; `a :~: b` just happens to be the GADT that corresponds precisely to the equality `a ~ b`. Using `(:~:)`, we can write a reusable proof that `IsSubKind k 'Input` implies `ParserInput k ~ InputValue`: - -```haskell -inputParserInput :: forall k. IsSubKind k 'Input => ParserInput k :~: InputValue -inputParserInput = case subKindProof @k @'Input of - KRefl -> Refl - KBoth -> Refl -``` - -This function is a very simple proof by cases, where `Refl` can be read as “Q.E.D.”: - - * In the first case, matching on `KRefl` refines `k` to `'Input`, and `ParserInput 'Input` is `InputValue` by definition of `ParserInput`. - - * Likewise, in the second case, matching on `KBoth` refines `k` to `'Both`, and `ParserInput 'Both` is also `InputValue` by definition of `ParserInput`. - -This `inputParserInput` helper allows functions like `nullable`, which internally need `ParserInput k ~ InputValue`, to take the form - -```haskell -nullable :: forall k a. IsSubKind k 'Input => GQLParser k a -> GQLParser k (Maybe a) -nullable parser = case inputParserInput @k of - Refl -> {- ...implementation goes here... -} -``` - -Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without `IsSubKind` doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight! - -# Wrapping up and closing thoughts - -So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points: - - * Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types. - - * Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly. - - * Though I’ve called the technique “*typeclass* metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming. - - * Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can. - -The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more. - -Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way. - -It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas: - - * As mentioned earlier in this blog post, [the `GHC.Generics` module documentation][hackage:base:GHC.Generics] is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use. - - * I have long believed that [the GHC User’s Guide][ghc] is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system. - - * Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the [singletons][hackage:singletons] library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, [the accompanying paper][rae:singletons] is definitely worth a read if you’d like to go down that route.) - -Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy. - - -[^1]: Not to be confused with C++’s [*template* metaprogramming][wiki:cpp-tmp], though there are significant similarities between the two techniques. - -[^2]: There have been proposals to introduce ordered instances, known in the literature as [*instance chains*][mpj:instance-chains], but as of this writing, GHC does not implement them. - -[^3]: Note that this also preserves an important property of the Haskell type system, parametricity. A function like `id :: a -> a` shouldn’t be allowed to do different things depending on which type is chosen for `a`, which our first version of `guardUnit` tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. - -[^4]: Short for *generalized algebraic datatypes*, which is a rather unhelpful name for actually understanding what they are or what they’re for. - -[^5]: If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for `IsEvenTF`: - - ```haskell - type family IsEvenTF as :: Constraint where - IsEvenTF '[] = () - IsEvenTF (a ': as) = exists b as'. (as ~ (b ': as'), IsEvenTF as') - ``` - - The type refinement provided by matching on `HCons` would be enough for the second case of `IsEvenTF` to be selected, which would provide an equality proof that `as` has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. - -[^6]: Actually, I’ve cheated a little bit here, because `unsingleton unitList` really does typecheck in GHCi under normal circumstances. That’s because [the `ExtendedDefaultRules` extension][ghc:extended-default-rules] is enabled in GHCi by default, which defaults ambiguous type variables to `()`, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned `ExtendedDefaultRules` off to illustrate the problem. - - -[blog:axioms]: /blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/ -[blog:pdv]: /blog/2019/11/05/parse-don-t-validate/ -[ghc]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ -[ghc:closed-type-families]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families -[ghc:data-kinds]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html -[ghc:derive-generic]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric -[ghc:extended-default-rules]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules -[ghc:gadts]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html -[ghc:ghci-kind]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind -[ghc:overlapping-instances]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances -[ghc:type-applications]: https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html -[graphql]: https://graphql.org/ -[hackage:base:Data.Type.Equality]: https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html -[hackage:base:GHC.Generics]: https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html -[hackage:mono-traversable]: https://hackage.haskell.org/package/mono-traversable -[hackage:servant]: https://hackage.haskell.org/package/servant -[hackage:singletons]: https://hackage.haskell.org/package/singletons -[hasura]: https://hasura.io/ -[mpj:instance-chains]: https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf -[rae:singletons]: https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf -[wiki:cpp-tmp]: https://en.wikipedia.org/wiki/Template_metaprogramming diff --git a/blog/posts/2025-05-29-a-break-from-programming-languages.md b/blog/posts/2025-05-29-a-break-from-programming-languages.md deleted file mode 100644 index d0abd6d..0000000 --- a/blog/posts/2025-05-29-a-break-from-programming-languages.md +++ /dev/null @@ -1,129 +0,0 @@ - Title: A break from programming languages - Date: 2025-05-29T00:00:00 - Tags: programming languages, personal - -This is a blog post I have been considering writing for a long time. - -People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior. - -My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back. - -This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for *me*, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why *not*—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way. - -# Whence programming languages - -I have had a personal fascination with programming languages for as long as I have been using them. I made my first serious forays into programming in the fifth grade, writing (very simple and mostly very bad) Flash games in ActionScript 3. I wrote my first programming language interpreter in my sophomore year of high school, a dynamically-typed, object-oriented Lisp with prototypal inheritance. The interpreter was written in C, and despite it being my first serious endeavor with the language, I used preprocessor macros to implement a form of exceptions in terms of `setjmp.h`. It is an amusing project in retrospect, given what my design sensibilities would eventually turn out to be, but it at least provides a little insight into how much the subject was already on my mind in my teenage years. - -I have always deeply enjoyed programming. That is no less true now than it was fifteen years ago. As someone with (then untreated) ADHD, the automation of repetitive tasks has always held immediate appeal, but it did not take long to discover I enjoy programming for its own sake. It is inherently delightful to me to direct an infinitely reconfigurable machine towards any end I desire, given only a little thought and some time at a keyboard. Program *design*, even in the large, was something that immediately appealed to me. It is often as much a challenge of organizing one’s thoughts as it is a matter of translating them to a form the computer can understand, and the former is a challenge I have always found rewarding. - -Even so, as every programmer inevitably discovers, the gap between one’s thoughts and executable code is never so narrow as we would like. I quickly discovered that a distressingly large amount of my time was being spent on the exactly the sort of repetitive task I had hoped to avoid, mechanically typing out reams of boilerplate in order to carry out what seemed to be incredibly simple tasks.[^1] It felt strange that we, as programmers, could be one of the lucky disciplines equipped with the means to produce our own tools, yet the tools we work with are so frequently unsatisfactory. - -[^1]: Readers may find it clarifying to note that a great deal of my early programming experience was writing (then era-appropriate) Java 6. - -Programming is an astonishingly young discipline. FORTRAN, arguably the first recognizable programming language, is less than seventy years old, and the first stored-program computers are only a decade older. Programmers have been trying to bridge the gap between our conceptualizations of programs and the (often laborious) act of programming for as long as computing has existed. As John Backus said in 1979, - -> Much of my work has come from being lazy. I didn’t like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs. - -In my experience, this sentiment perfectly captures the strange relationship many programming language enthusiasts have with our craft. We enjoy programming so much that we are willing to spend enormous time, thought, and effort working on programming systems precisely to free ourselves from the tortuous burden of writing programs. - -One possible means of reconciling this apparent contradiction would be to hypothesize that programmers like me or like Backus enjoy the *results* of our programs but not the process of writing them. We want to get things done, and we view the computer as a useful but necessary evil that should get out of the way as much and as expediently as possible. However, although I am sure there are many programmers who do match that description, I do not count myself among them, and I would likewise find it extremely difficult to believe that Backus—a man who dedicated his life to designing ever more sophisticated means of expressing computer programs—was someone fundamentally uninterested in programming. - -On the contrary, I see a great deal of work on programming languages as a struggle—often in vain—by those of us who love programming so much that we wish to free it of its unfortunate imperfections. We strive to remove the impediments and obstacles we face when translating the beautiful, platonic programs we hold in our heads into the comparatively cold, alien language of silicon calculating machines. There is a widespread mythology that programming languages are primarily abstractions over machine code, but this perspective has been misguided for almost as long as programming languages have existed. Programming languages are instruments of thought, frameworks for precisely specifying an executable idea, and they are designed almost entirely for *humans*, with computers often providing little more than a loose set of inconvenient restrictions. - -## To compute is human, to program divine - -The earliest computers were developed with a singular purpose in mind: to compute. A program was, essentially, a sequence of instructions containing the computations to be performed. This conceptualization of a computer program as essentially analogous to a cooking recipe or assembly manual has remained curiously persistent, even to the present day. - -Almost no modern programs of any complexity look anything like a cooking recipe. Even programs written in the most imperative programming languages under the sun are complex webs of logic specifying dataflow, access policy, and interaction patterns between semi-autonomous, reactive subsystems, many of which come equipped with their own living, beating hearts. At the micro scale, it is true that programs specify behavior as a series of steps to be performed, but even this is a fiction maintained by modern compilers for humans’ convenience. Through a compiler’s eyes, the *real* programs are whatever is left after the comforting fiction is boiled away to obtain a homogenous pile of SSA and dataflow graphs to be haphazardly reassembled into a patchwork of basic blocks in a language most programmers would not even begin to understand. - -Indeed, in stark contrast to the mental model of the programmer as a sort of silicon whisperer, I believe most practicing software engineers are primarily maintaining vast behavioral specifications whose defining sense of rigor comes not from computers *per se* but from the consistency we demand from their results. In other words, we are trained to be fluent speakers of languages that allow us to articulate a solution to a problem with a level of precision that ensures every legitimate interpretation is and will always be to our satisfaction. Crucially, these specifications do not *fully* specify how the task is to be carried out. Optimizing compilers are free to make their own choices with whatever leeway we permit them. HTML/CSS user agents may interpret our instructions in whatever way preserves whatever web standards happen to require. Even RDBMS query planners, a foundational technology largely unchanged for the past several decades, are essentially expert systems designed to decide on the fly how best to execute searches and retrievals under ever-changing conditions. - -This is not to say that the task of programming is fully removed from the machine that ultimately executes our instructions, nor is it even really a suggestion it ought to be. Performance and debugging mandate a certain familiarity with the underlying system, even if the full complexities of its true inner workings remain carefully hidden from even its expert users. My point is simply that programs are not rote algorithms, and the act of programming is not primarily characterized by algorithmic thinking. Rather, programming is fundamentally a game of domain modeling and system design, with bits of algorithmic logic inserted here and there to glue the whole thing together. - -My interest in pushing the frontier of programming language design has always been and continues to be an interest in making the task of writing programs feel closer to the task of designing them. In an ideal world, I would have a language expressive enough to more or less directly translate the computer programs I have in my head (which, for me, more often than not take *visual* form) into a digitized structure a computer may faithfully execute. It is that task I found myself fixated on at the tender age of fifteen, and it is one I would remain fixated on for the better part of the following decade. - -# Ten years of programming languages - -I turned twenty eight this year. Almost exactly ten years ago, I started my first professional software engineering job. It was a fairly mundane position writing Ruby on Rails and JavaScript, but even then, I spent a great deal of free time pursuing the programming language hobby projects that truly caught my fancy. My first professional experience working with Haskell would come less than a year later, experience that would soon inspire my hobby research project to implement a Haskell-style type system within the Racket macro system. The majority of my career to date has followed more or less directly from that formative time. - -After a decade, it seems fair to reflect a little on what I have learned, what I managed to achieve, and maybe more significantly, what I did *not*. Something one learns very quickly upon beginning any serious work on programming languages is that they are startlingly, unforgivably *hard*. A vast array of dizzyingly smart people have been working on the problem of program specification for seventy years; there are not many pieces of low hanging fruit left on the tree, and any new gains tend to be very hard won. - -Still, even if programming language design is challenging, it would be some consolation if we were able to slowly yet steadily advance the state of the art, gradually picking away at the gap between the tools we have now and the ultimate programming language of the future. Sadly, even that pragmatic vision does not survive contact with reality. In practice, the challenge of programming language design is not one of expanding a well-defined frontier, it is grappling with a neverending list of fundamental *tradeoffs* between mutually incompatible features. - -Subtyping is tantalizingly useful but makes complete type inference incredibly difficult (and in general, provably impossible). Structural typing drastically reduces the burden of assigning a *name* to every uninteresting intermediate form but more or less precludes taking advantage of Haskell-style typeclasses or Rust-style traits. Dynamic dispatch substantially assists decoupling of software components but can come with a significant performance cost without a JIT. Just-in-time compilation can use runtime information to optimize code in ways that permit more flexible coding patterns, but JIT performance can be unpredictable, and the overhead of an optimizing JIT is substantial. Sophisticated metaprogramming systems can radically cut down on boilerplate and improve program concision and even readability, but they can have substantial runtime or compile-time overhead and tend to thwart automated tooling. The list goes on and on. - -The essential, most stubborn problems in programming languages come from unavoidable tensions between conflicting desires and requirements. We want loosely coupled software components that can be easily reused, but we also want the performance benefits of tight coupling and specialization. We want flexible programming languages that do not impose upon our freedom of expression, but we also want the benefits of static program analysis and powerful safety guarantees. We want sophisticated type systems that allow specifying ever more complex invariants, but we also want readable type signatures that won’t regularly end up longer than the code itself. - -We cannot build the One True Programming Language because we cannot have everything at once, and we cannot hope to universally decide which tradeoffs are the right ones to make. This naturally makes the thought of a system constructed from a tapestry of many different programming languages attractive, affording the programmer the opportunity to select the tool best suited to the task at hand. Unfortunately, that, too, comes with (often brutal) tradeoffs of its own: the impedance mismatch at language boundaries, the vastly more complex tooling required for a polyglot system, and the increased knowledge burden of maintaining a system built in so many different ways. - -Ten years of working on and thinking about programming languages has forced me to come to terms with a humbling truth: I do not know how to build a better programming language. I am personally extremely sympathetic to the idea that the future of programming probably involves more domain-specific languages, and I think the industry as a whole has been (very slowly) trending in that direction for quite some time. But even I acknowledge that the question of how precisely that ought to be done is extraordinarily complicated, and there are no easy answers here. - -Even so, it would be far too dismal of me to suggest that the problem is hopeless, nor do I believe the work does not matter. Even if the problem has proven more difficult than teenage me may have hoped, the work *is* satisfying when it bears fruit. Unfortunately, even after toiling for years overcoming daunting problems, every working programming language enthusiast is forced to confront a much more frustrating enemy: *programmers*. - -## The reactionary conservatism of the median programmer - -It should come as no surprise that programming language design is largely a social problem. Choice of programming language tends to be driven by strong network effects, and programmers tend to prefer languages that resemble what they already know. Programmers’ general lack of curiosity and outright hostility to learning new things has always been a personal frustration of mine, but I ultimately do understand much of where it comes from: as engineers, an overwhelming part of our job description is managing *risk*. Exotic technology choices are risky, and it is a responsible impulse to be wary of them. - -Still, the history of mainstream programming languages is essentially a story of programmers vocally and emphatically rejecting what eventually proved to be some of the most incredibly successful innovations in the history of the field. Assembly programmers largely laughed at FORTRAN, but just a few decades later, there were nevertheless very few remaining assembly programmers. First-class functions were widely derided as needlessly complicated and confusing until programmers were forced to finally take the time to learn to use them once JavaScript became a load-bearing language by historical accident, and within a decade, they became a required feature for every major programming system. Sophisticated type systems largely retain a perception of overengineered, ivory-tower elitism, but many of the programmers who hold those very opinions have enthusiastically adopted Rust, a language that features a type system so complex that idiomatic Rust code can easily put Haskell programs to shame. - -Discussions of programming languages are, by and large, emotionally driven shouting matches based on anecdotes and gut feelings. Surprisingly (or perhaps not), although most working programming language theorists and compiler engineers have a far more informed perspective on this subject (and do at least tend to refrain from petty debate), disparaging remarks directed towards languages not to the speaker’s taste are hardly unheard of even at academic conferences dedicated to the subject. I want to be very clear that I do not think that is somehow indicative of some toxicity within the community—they are by and large a group of truly amazing, wonderful people, and such comments are usually delivered with tongue planted firmly in cheek. All the same, my point is simply that programming languages are an emotional, opinionated subject in a way that seems perhaps inherent to the craft, [a topic I explored at some length all the way back in 2019.](/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/) - -I do earnestly believe that the field of software engineering would benefit from more openness to new ideas and willingness to experiment with new technologies. I also think it would be enormously refreshing if programmers did not nearly universally speak with an expert’s confidence on subjects they have only passingly encountered.[^2] Nevertheless, I do not expect programmers’ tribalism to diminish anytime soon, so the pragmatist in me understands their nature imposes real limitations on what we as a field can meaningfully accomplish. - -[^2]: I have often wondered if other engineering disciplines have anything to rival the collective overconfidence of the average Hacker News commenter. This is an earnest curiosity, not merely a rhetorical flourish; I do sometimes wonder what it is about my field of choice that engenders such disregard for the value of expertise. - -In my experience, most working programming language researchers have essentially resigned themselves to this fact, and the prevailing wisdom is to almost entirely decouple the value of the work from its impact. Even if an idea is broadly ignored by programmers today, if it’s a good enough idea, it will still make its way into some programming language in some distant tomorrow, as many ideas eventually have. Lexical closures were first applied to programming languages in 1975 but took a full thirty years to be broadly accepted. Algebraic datatypes were first introduced around the same time and have only recently found mainstream acceptance through Rust. Innovations take a long, *long* time to make the jump from research to practice in this field, and placing one’s sense of professional accomplishment in the fickle hands of mainstream programmers’ whims and preferences is a recipe for personal misery. - -I have nothing but respect for this attitude, genuinely. This research community is astonishingly friendly, warm, creative, fun, and accepting, especially given the vitriol these subjects tend to elicit from programmers at large. I feel genuinely honored to have had the opportunity to personally collaborate with many of them, and the collective output of endlessly fascinating ideas and exciting projects one can find each and every year at every major PL conference is consistently inspiring. All the same, as much respect as I have for the people who work tirelessly on these problems for their own sake, I am not sure I can continue to count myself among them. I do find the subject exciting, and I do consider it rewarding in its own way, but these past few years, I have slowly been coming to terms with the fact that I don’t think I find it quite rewarding enough. - -# Why I write computer programs - -I like writing software. It is strange how radical a statement that sometimes feels. So many programmers seem to practically despise what they do for a living, at least as you’d hear them describe it. I have never really been able to empathize with that mindset. I find programming fun, and I derive a unique joy from writing code that decisively solves the problem at hand. It doesn’t have to be a particularly interesting problem, and the solution doesn’t even have to require much more than snapping blocks together, but as long as I can take some pride and satisfaction in the craftsmanship and end result, I am usually pretty happy. - -My attraction to libraries, tooling, and compilers has always stemmed from a mixture of personal motivation and a certain satisfaction from improving things for my peers. One of my favorite things about software engineering is taking a problem and solving it in such a way that it largely *stays solved*. Sure, it may need to change as requirements evolve, but the solution continues to work even after the labor is done. Other tasks in my life do not provide nearly the same sense of accomplishment. When I do a load of laundry, it provides me with clean clothes for a week or two, but ultimately, I know it won’t be long before I’m going to have to do it again. Software engineering is not like that. Working on software—even extremely mundane software—provides a pleasant variety of tasks that can be well and truly *completed*. I like that sense of progress, and I appreciate the relaxing, almost meditative process of growing and maintaining the metaphorical garden that is a collection of code. - -Given all of the above, it is not altogether clear why I would struggle to take satisfaction in my work on programming languages. If anything, working on a compiler seems like the ultimate realization of my aforementioned desires applied to the very thing I, myself, spend my day doing. If there is some annoying or repetitive task, and I can extend my programming system to provide a better way of doing it, that task is something I will never again need to do. What’s more, working on a compiler that thousands of people actually use means this is not just benefiting me, it’s benefiting everyone else as well. It feels good to have that kind of impact, and to know that my effort is essentially multiplied by everyone who happens to use it. - -On paper, this ought to make working on a compiler like GHC enormously satisfying. Even if I feel frustrated by mainstream attitudes towards programming languages, Haskell programmers feel differently. Surely, working on something used and appreciated by Haskellers ought to feel very rewarding. But it wasn’t really. There were parts that were nice, yes, and the problems were often interesting. But I didn’t find it *rewarding* in the way I had hoped, and I’m not sure it even compared favorably to working on bog-standard software. Why? - -## On the Haskell community - -This was the hardest section of this blog post to write. It is easy to write about the things I love and the people I admire. It is perhaps even easier to write about the things I despise and the people I disdain. The Haskell community is neither. I do not *dislike* it. Several of its members are people I consider very good friends. Nonetheless, I have never managed to fall in any particular love with it, and even if that is no sin on its part, it is a somewhat sad thing to have to confess. - -I want to be *excruciatingly* clear that I do not wish to mount any indictment of the Haskell community or any of the people in it. Many people seem to have a preconception of Haskellers as mean and elitist, and while I do get the sense there have been some historically bad actors that contributed to that perception, my general impression is that those people have been thoroughly expunged. I have never been the target of any memorably rude conduct, and I’ve definitely never felt targeted by any prejudice or discrimination, and if anything I have always felt universally warmly welcomed at every meetup or conference I have attended. I cannot speak for anyone but myself, and my experiences are necessarily limited. I cannot hope to claim with any certainty that no abuse has ever occurred or that it does not continue to occur within any Haskell’s myriad social spaces, most of which I have never participated in. Still, I am happy to be able to say that I have never personally experienced any real unfriendliness, and that is certainly not a factor in my dispassion for the language’s community. If anything, I feel I must extend a very genuine *thank you* for all the lovely support I have received from Haskellers over the years, and it is that support and enthusiasm that so often kept me motivated for as long as I was. - -This is all simply to say that I do not think it is the *fault* of anyone in particular for my lack of affinity for the Haskell community. Rather, I think perhaps it is just not a culture that I have ever especially related to. That probably does not even say very much, as I’m not sure there are many communities I *could* say that about. That says much more about me than it does about Haskell or Haskellers. - -It is admittedly tempting to say that perhaps I find these communities alienating in part because they lack people I identify with and relate to. After all, it is simply true that the Haskell community is *overwhelmingly* male, which really has felt lonely at times. It’s also true that the Haskell community tends to attract a lot of people who are mainly interested in Haskell because they find it intellectually interesting, or because they like category theory and enjoy libraries like `lens` that have explored those ideas, while I am really only interested in Haskell insofar as it is a practical vehicle for writing useful software. Nevertheless, there *are* other women who write Haskell and there *are* other people who care very much about writing useful software in Haskell, and that doesn’t seem to have radically altered my relationship to the community, so I think it would be disingenuous of me to claim that either of those flaws are substantially to blame. - -If there is one substantive frustration I can express with Haskell, it is where the money comes from. For as long as I have been involved with it, Haskell has predominantly been used and funded by a combination of fintech and cryptocurrency. I don’t really have anything against fintech, though I certainly don’t find it especially exciting, but I do have something against crypto, and those who followed me on Twitter before its untimely demise know I have always been pretty open about that fact. My thoughts on the matter are really not something I want to elaborate on here, but suffice to say it is not especially motivating to work on a language if all of its users are applying it in ways I feel essentially indifferent about at best and actively hostile to at worst. This is obviously not something I have any easy answers to, but it is probably the most significant, concrete factor in me losing my interest in working on GHC, so if you mourn me leaving, at least I can give you one semi-actionable reason. - -Make no mistake: GHC is a truly incredible project that often still feels like a compiler from the future, and having the opportunity to work on it as a part of my job was about as technologically stimulating as I could possibly ask for. Even so, during my time at Tweag, I often found myself reflecting on the fact that the *users* I was working for were, ultimately, Haskell programmers, a class I’m not sure even really contains *me*. I have often reflected on the fact that, although I have many personal projects, I have *never* chosen Haskell for any of them. Not once. Whenever I need to write a little code to accomplish some task, it’s always Racket. This blog is in Racket. My personal shell utilities are in Racket. When I want to scrape a website or wrap some library, I do it with Racket. Haskell is a language I have always exclusively written professionally, and although I do very earnestly love many things about it, it is not so precious to me that I feel motivated to contribute to it out of passion alone. If there were more *users* of Haskell doing inspiring, exciting things with the tools I was working on, I might feel substantially more driven to indirectly contribute to those causes. But Haskell is a niche language used by a select few, and it is hard for me to spend so much of my life working on a technological marvel that practically nobody will ever benefit from. - -As a final note on this subject: I do believe very earnestly that the functional programming and programming languages communities would benefit enormously from more demographic diversity. Even compared to software engineering as a whole, the gender ratio is, frankly, almost unbelievably grim. I have always been drawn to interests and hobbies that tend to skew male, and that has never really bothered me, so if *I* have found myself feeling alienated by the conspicuous absence of other women—to the point that, in the past, I have sometimes considered giving up on Haskell primarily for that reason—I don’t think it is outrageous to say things are pretty dire. A wider set of perspectives could, in theory, inject some refreshing creativity into the dreary ecosystem that is industrial Haskell. Of course, I will admit that I have absolutely no idea how that end could possibly be achieved, and it is a subject far broader and more complicated than I think belongs in this blog post. For now, I will leave things at that, but I hope my voice is sufficiently well regarded that some readers may take my comments to heart. - -# Reflections on a decade’s work - -Before writing this blog post, I essentially pitched it to a group of friends as “a sort of apology”. At times I certainly feel burdened by the weight of all the endeavors I’ve left unfinished, all the work I never carried long enough to bear fruit. Even so, as I write these words, I find myself reflecting on the things I feel I *did* manage to contribute these past ten years, and it strikes me that the trail I’ve left behind is perhaps not so dismal, after all. - -It is comforting to look back on a decade’s accumulations and realize there is enough to take some real pride in. I am proud of my contributions to Racket and Haskell, and of my numerous libraries in each of them. I am proud of my experiments, among them [Hackett](https://github.com/lexi-lambda/hackett) and [eff](https://github.com/lexi-lambda/eff), and the excellent work from others they have inspired. I am proud of this very blog, from the [most impactful](/blog/2019/11/05/parse-don-t-validate/) things I’ve written to the [far more niche](/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/). I am proud of my [contributions to Programming Languages Stack Exchange](https://langdev.stackexchange.com/users/861/alexis-king?tab=answers&sort=newest), a site I most certainly intend to remain a moderator of and will undoubtedly continue to contribute to. And I am proud of the [numerous talks](https://www.youtube.com/watch?v=0jI-AlWEwYI) I have [presented over the years](https://www.youtube.com/watch?v=TE48LsgVlIU), all of them labors of love. - -I have not always accomplished all the things I set out to achieve. Some have, for some reason or another, gotten the better of me. Many of them, from my [math typesetting system](https://github.com/lexi-lambda/blackboard) to my [improvements to GHC’s arrow notation](https://github.com/ghc-proposals/ghc-proposals/pull/303), are things I hope to one day push over the finish line. But for now it is clear my heart is no longer really in them, so I think it is time for me to let them go. Perhaps others will find inspiration in them. Perhaps you will succeed where I did not. - -It is a little sad to think about moving on from such a substantial chapter of my life. To date, I have spent almost my entire career in this professional niche, and I suspect I will never fully leave it. It has been a pleasure to work with so many bright, lovely, inspiring people, and perhaps one day I will find myself eager to return once more. But for now, my interests are elsewhere, and despite the bittersweet feelings, there is also a very real excitement in the tantalizing opportunity to do something new. - -# What next? - -It is a little amusing to think that what I find myself most drawn to after a decade of pursuing ever more exotic projects is something entirely mundane: I want to write utterly ordinary software. I want to work on a piece of software used by people to accomplish a task, and I want to take pride and satisfaction in doing it well. - -I do not yet really have any idea what that might look like. It has been over six years since I last worked on anything that really feels like it could possibly fit that description, and even then, it was only one part of my job. Is it so strange that after spending fifteen years of my life trying everything I could to get away from spending all day wrangling a big ball of mud in a Java IDE, that experience sounds pretty cozy to return to? - -In late 2022, I decided on a whim to reverse engineer and resurrect a 2008 Java browser game called [Shattered Plans](https://github.com/lexi-lambda/shattered-plans). While doing so, I had the opportunity to spend an awful lot of time in IntelliJ, and after years of acclimatizing to writing code in fanciful languages with nothing more than a plain text editor, a terminal emulator, and a dream, the experience was almost viscerally thrilling. They say the grass is always greener, but even if that is true, I think I’d like to find it out for myself. - -More to the point, my life is just very different now from how it was for most of my twenties. I was single, and it was easy to justify spending almost all of my time thinking about code, whether on the clock or not. It was hardly unheard of for me to spend twelve or even sixteen hours at my desk, just tinkering on something that had captured my attention. It was fun, and I’m glad I did it, but I am also very ready to work a simple, regular job I don’t feel the need to permanently devote a large portion of my soul to. It isn’t very romantic, but then, I have other things for that. - -If you think you have a position that meets those (admittedly extremely broad) criteria, and you think you might like to hire me, by all means: [send me an email](mailto:lexi.lambda@gmail.com). The worst I can do is not reply. And who knows? With the burden of my own expectations behind me, perhaps I’ll even blow the dust off this old blog of mine and start writing things again. As with my job, whatever I might choose to write next I cannot say. Either way, it’s a little exciting to be able to treat myself to a blank slate. - -I could probably spend twenty thousand words writing in circles about all my thoughts and feelings on this subject. However, to be honest, I don’t especially want to. As this blog post has hopefully communicated, I would like to be done, and so this will have to be enough. Allow me to personally thank all of you who have indulged me by reading my disorganized ramblings to the end. - -It’s been a long time. Here’s to another ten years. diff --git a/blog/posts/about-me.scrbl b/blog/posts/about-me.scrbl deleted file mode 100644 index b83c37e..0000000 --- a/blog/posts/about-me.scrbl +++ /dev/null @@ -1,67 +0,0 @@ -#lang blog/lang/base -@(begin - (require racket/list - threading) - - (define Scribble @seclink["top" #:doc '(lib "scribblings/scribble/scribble.scrbl")]{Scribble})) - -@title{About me} - -My name is Alexis King, and I write @hyperlink["https://github.com/lexi-lambda?tab=repositories"]{a lot of software}. I currently live in Chicago. - -I’m interested in functional programming, static types, and programming language research, and I try to spend as much time as I can writing Haskell and Racket. I write about some of the things I do @hyperlink["/"]{on this blog}, and I sometimes tweet about them and other things @hyperlink["https://twitter.com/lexi_lambda"]{on Twitter}. I work on a @emph{lot} of open-source projects @hyperlink["https://github.com/lexi-lambda"]{on GitHub}, and you can email me at @hyperlink["mailto:lexi.lambda@gmail.com" "lexi.lambda@gmail.com"]. - -@section{Things I’ve worked on} - -This list is just a smattering of the cooler things I’ve worked on over the past few years. It is neither exhaustive nor particularly scientific in its curation. - -@(define separator @elem[#:style "about-me-year-separator" "⁓"]) -@(define (year-events year . items) - @para[#:style "about-me-year-events"]{@elem[#:style "about-me-year-label" year] @separator - @(~> (map (λ (item) @elem[#:style "about-me-year-entry" item]) items) - (add-between (list " " separator " ") #:splice? #t))}) - -@year-events["2021" - @list{Reimplemented @github-repo*["lexi-lambda.github.io"]{this blog} on top of @Scribble, replacing @github-repo{greghendershott/frog}.} - @list{Released @github-repo{megaparsack} v1.5, adding support for user-defined parser state and parser lookahead.}] - -@year-events["2020" - @list{Published @hyperlink["https://dl.acm.org/doi/abs/10.1145/3428297"]{“Macros for DSLs”} with Michael Ballantyne and Matthias Felleisen at OOPSLA.} - @list{Presented @hyperlink["https://www.youtube.com/watch?v=0jI-AlWEwYI"]{“Effects for Less”} at ZuriHac 2020 on the design of @github-repo{hasura/eff} and its accompanying @hyperlink["https://github.com/ghc-proposals/ghc-proposals/pull/313"]{GHC proposal} to add delimited continuations to the RTS.} - @list{Led a large-scale refactoring effort to statically rule out several classes of bugs in @github-repo{hasura/graphql-engine}.} - @list{Opened @hyperlink["https://github.com/ghc-proposals/ghc-proposals/pull/303"]{a GHC proposal} for improving arrow notation.} - @list{Started contributing to GHC, including some @hyperlink["https://gitlab.haskell.org/ghc/ghc/-/merge_requests/3041"]{performance fixes} and @hyperlink["https://gitlab.haskell.org/ghc/ghc/-/merge_requests/3115"]{improvements to arrow notation}.}] - -@year-events["2019" - @list{Published @hyperlink["https://dl.acm.org/doi/abs/10.1145/3371133"]{“Does blame shifting work?”} with Lukas Lazarek, Samanvitha Sundar, Robby Findler, and Christos Dimoulas at POPL.} - @list{Released @hackage-package{monad-validate} based on work done for Hasura.} - @list{Started working at @hyperlink["https://hasura.io/"]{Hasura}.}] - -@year-events["2018" - @list{Presented @hyperlink["https://www.youtube.com/watch?v=g6UCeHiKodo"]{“Hackett: a metaprogrammable Haskell”} at Curry On, and again a few months later @hyperlink["https://www.youtube.com/watch?v=5QQdI3P7MdY"]{at Strange Loop}.} - @list{Started working on contract systems with Christos Dimoulas at Northwestern University.}] - -@year-events["2017" - @list{Released the @hackage-package{freer-simple} package.} - @list{Presented @hyperlink["https://www.youtube.com/watch?v=bOUgXd9XlJ4"]{“Hackett, a Haskell for Racketeers”} at RacketCon.} - @list{Started working on @github-repo*["hackett"]{Hackett}, a Haskell-like language embedded in Racket.}] - -@year-events["2016" - @list{Started learning about type systems to explore ideas inspired by @seclink["top" #:doc '(lib "turnstile/scribblings/turnstile.scrbl") #:indirect? #t]{Turnstile}.} - @list{Presented @hyperlink["https://www.youtube.com/watch?v=TfehOLha-18"]{“Languages in an Afternoon”} at RacketCon.} - @list{Released the @github-repo{megaparsack} and @github-repo{scripty} Racket libraries.} - @list{Started writing Haskell professionally for the first time.}] - -@section{About this blog} - -This blog is powered by @Scribble, an unusually flexible document preparation system written in @hyperlink["https://racket-lang.org/"]{Racket}. Unlike most markup languages, every Scribble document is a @emph{program} that evaluates to a document, which can then be rendered using one of a number of different backends. Scribble provides a TeX-like notation for writing such programs, but unlike TeX—which is essentially an overgrown macro preprocessor—Scribble is a full-fledged functional programming language. In fact, the Scribble syntax is really just an alternate notation for Racket itself, so all the libraries and abstractions available in Racket can be used more or less directly in a Scribble document. - -This makes Scribble a remarkably powerful tool for writing prose documents, and indeed, it serves as the foundation for Racket’s @hyperlink["https://docs.racket-lang.org/"]{best-in-class documentation system}, among other things. Using it to power a blog is perhaps a bit overkill, but it gives me the wonderful ability to define whatever abstractions I desire to make blogging as effortless as possible. For example, after finding myself linking to Hackage packages quite frequently, I decided to define a one-line function: - -@pygments-block[#:language "racket"]{ -(define (hackage-package package-name) - (hyperlink (string-append "https://hackage.haskell.org/package/" package-name) package-name))} - -Now all I have to do is write @code["@hackage-package{lens}"] and I get @hackage-package{lens}. Sure, it’s not exactly mind-blowing, but it’s certainly convenient… and of course, the most significant advantages involve abstractions too elaborate to describe here. - -This site is, naturally, open source, so if you’d like to see how all the pieces fit together for yourself, feel free to clone @github-repo*["lexi-lambda.github.io"]{the GitHub repository}. And if you’re interested in a simple example of what Scribble looks like to use, you might as well take a peek at @hyperlink["https://github.com/lexi-lambda/lexi-lambda.github.io/blob/source/blog/posts/about-me.scrbl"]{the source code for this page in particular}. diff --git a/css/application.min.css b/css/application.min.css new file mode 100644 index 0000000..45f45a7 --- /dev/null +++ b/css/application.min.css @@ -0,0 +1,345 @@ +@charset "UTF-8"; +pre, code { + font-family: "Fira Code", monospace; + font-variant-ligatures: none; } + +p code, li code, table code { + background-color: #fffafa; + border-radius: 3px; + padding: 2px 5px; } + +pre code { + background-color: #fffafa; + border-radius: 10px; + display: block; + font-size: 0.9em; + line-height: 1.75em; + margin: 1em auto; + overflow: auto; + padding: 20px; + -webkit-overflow-scrolling: touch; } + @media (min-width: 800px) { + pre code { + margin: 2em auto; } } + +html { + background-color: #fffcfc; + font-family: "Merriweather", serif; } + +* { + box-sizing: border-box; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; } + +html, body { + margin: 0; + padding: 0; + height: 100%; + width: 100%; } + +body { + display: flex; + flex-direction: column; + width: 100%; + min-height: 100%; } + +h1, h2, h3, h4, h5, h6 { + font-family: "Merriweather Sans", sans-serif; + font-weight: 400; } + +h1 { + font-size: 2em; + font-weight: 700; + line-height: 1.4; + margin-bottom: 0.4em; } + @media (min-width: 575px) { + h1 { + font-size: 2.3em; } } + +h2 { + font-size: 1.75em; + line-height: 1.5; + margin-top: 2em; } + @media (min-width: 800px) { + h2 { + font-size: 2em; } } + +h3 { + font-size: 1.5em; } + +h4 { + font-size: 1.17em; } + +h5 { + font-size: 1.1em; } + +a { + text-decoration: none; + transition: color 0.15s ease-in-out; } + +p, article li, blockquote { + margin-bottom: 1.2em; } + @media (min-width: 800px) { + p, article li, blockquote { + margin-bottom: 2em; } } + +body > section[role=main] { + color: #312323; + flex: 1; + font-size: 1.1em; + font-weight: 300; + line-height: 2; + padding: 0 30px; } + @media (max-width: 550px) { + body > section[role=main] { + font-size: 0.9em; } } + body > section[role=main] > .content { + margin: 0 auto; + padding: 40px 0; } + @media (max-width: 550px) { + body > section[role=main] > .content { + padding: 30px 0; } } + body > section[role=main] article > * { + margin-left: auto; + margin-right: auto; + max-width: 700px; } + body > section[role=main] article > h1, body > section[role=main] article > h2, body > section[role=main] article > h3, body > section[role=main] article > h4, body > section[role=main] article > h5, body > section[role=main] article > h6 { + max-width: 770px; } + body > section[role=main] article > header { + max-width: 850px; } + body > section[role=main] article > header .date-and-tags { + max-width: 700px; + margin-left: auto; + margin-right: auto; } + body > section[role=main] article > pre, body > section[role=main] article > .table-wrapper { + max-width: 100%; + width: 100%; } + @media (min-width: 760px) { + body > section[role=main] article > pre, body > section[role=main] article > .table-wrapper { + min-width: 700px; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; } } + @media (min-width: 1060px) { + body > section[role=main] article > pre, body > section[role=main] article > .table-wrapper { + max-width: 1000px; } } + body > section[role=main] article > .post-navigation { + max-width: 1000px; } + body > section[role=main] sup, body > section[role=main] sub { + line-height: 0; } + body > section[role=main] a { + color: #c54747; } + body > section[role=main] a:hover { + color: #d16e6e; } + body > section[role=main] blockquote { + padding: 1em 1.5em 1em 2em; + border-left: 5px solid #f3ecec; + background-color: #fffafa; } + body > section[role=main] blockquote > :first-child { + margin-top: 0; } + body > section[role=main] blockquote > :last-child { + margin-bottom: 0; } + body > section[role=main] .table-wrapper { + display: block; + margin: 1em auto; + overflow: auto; + -webkit-overflow-scrolling: touch; } + @media (max-width: 700px) { + body > section[role=main] .table-wrapper { + font-size: 0.9em; + line-height: 1.9; } } + @media (max-width: 500px) { + body > section[role=main] .table-wrapper { + font-size: 0.8em; + line-height: 1.8; } } + @media (min-width: 800px) { + body > section[role=main] .table-wrapper { + margin: 2em auto; } } + body > section[role=main] .table-wrapper table { + border-collapse: collapse; + border-spacing: 0; + border: 3px solid #fef8f8; + width: 100%; } + body > section[role=main] .table-wrapper table thead tr, body > section[role=main] .table-wrapper table tr:nth-child(2n) { + background-color: #fef8f8; } + body > section[role=main] .table-wrapper table td, body > section[role=main] .table-wrapper table th { + border: 0; + padding: 0.6em 1.5em; } + body > section[role=main] hr { + border: none; + display: block; + margin-bottom: 2em; + margin-top: 2em; + overflow-x: hidden; + width: 100%; } + @media (min-width: 800px) { + body > section[role=main] hr { + margin-bottom: 3em; + margin-top: 3em; } } + body > section[role=main] hr::before { + content: '◆◆◆'; + color: #e2caca; + display: block; + margin: 0 auto; + text-align: center; + letter-spacing: 6em; + margin-right: -6em; + font-size: 0.9em; } + +.date-and-tags time { + font-size: 1.5em; } + +body > footer { + background-color: #e45b5b; + color: #fadede; + font-family: "Merriweather Sans", sans-serif; + font-size: 0.8em; + font-weight: 300; + padding: 10px 0; + text-align: center; } + body > footer .copyright-notice { + font-size: 1.45em; } + body > footer a { + color: inherit; + font-weight: bold; + text-decoration: underline; } + body > footer > * { + margin: 0.8em; } + +article.main .title { + margin-top: 0; } + +article.main .date-and-tags { + margin-bottom: 2em; } + +article.inline { + margin-bottom: 4em; } + article.inline .date-and-tags { + margin-bottom: 1em; } + article.inline .read-more-text { + font-style: italic; + margin-right: 0.25em; } + +.footnotes { + font-size: small; + line-height: 2; + margin-top: 4em; } + .footnotes li:target { + background-color: #fff5f5; + border-radius: 3px; + box-shadow: -1.5em 0 0 0.5em #fff5f5, 0.5em 0 0 0.5em #fff5f5; } + +article.main > footer { + margin-top: 40px; } + +.tag-page-header { + font-weight: normal; + margin-left: auto; + margin-right: auto; + margin-top: 0; + max-width: 1000px; } + +.navigation-bar { + align-items: center; + background-color: #e45b5b; + display: flex; + font-size: 1.2em; + font-family: "Merriweather Sans", sans-serif; + font-weight: 300; + width: 100%; } + .navigation-bar a { + color: #fadede; + display: block; + letter-spacing: 0.1em; + text-transform: uppercase; + text-decoration: none; } + .navigation-bar a:hover { + color: white; } + .navigation-bar .blog-title-header { + margin-bottom: 5px; + margin-top: 5px; + font-size: 1.5em; + font-weight: inherit; } + .navigation-bar .blog-title-header a { + letter-spacing: 0.075em; } + @media (max-width: 575px) { + .navigation-bar .blog-title-header { + display: none; } } + +.navigation-items { + padding: 0; } + .navigation-items.left, .navigation-items.right { + margin: 15px; } + .navigation-items.center { + flex: 1; } + .navigation-items li { + display: inline-block; + margin: 0 20px; } + +.pagination { + align-items: center; + display: flex; + justify-content: center; + margin-top: 4em; + padding: 0; + width: 100%; } + .pagination li { + flex: 1; + list-style: none; + margin: 0 8px; + max-width: 3em; + min-width: 3em; + text-align: center; } + @media (min-width: 450px) { + .pagination li { + margin: 0 15px; } } + .pagination li.disabled { + color: #ac9c9c; } + .pagination li.pagination-number { + border: 1px solid #e45b5b; + max-width: 5em; } + .pagination a { + display: block; + transition: background-color 0.25s ease-in-out; + width: 100%; } + .pagination li.active a, .pagination li.disabled { + cursor: default; } + .pagination a:hover, .pagination li.active a { + background-color: #e45b5b; + color: white !important; } + +.post-navigation { + display: flex; + flex-wrap: wrap; + margin-top: 4em; + padding: 0; } + .post-navigation li { + flex: 1 1 auto; + list-style: none; + margin-bottom: 1em; } + .post-navigation .previous { + text-align: left; } + .post-navigation .next { + text-align: right; } + .post-navigation .post-title { + font-style: italic; } + +div.figure > img, article.main > p > a > img { + max-width: 100%; } + +.no-line-wrapping { + white-space: nowrap; } + +.about-me-year-events { + margin-bottom: 0.35em; + margin-top: 0em; + padding-left: 1.2em; + text-indent: -1.2em; } + +.about-me-year-label { + font-weight: bold; + font-size: 1.1em; } + +.about-me-year-separator { + margin: 0.25em; } diff --git a/css/pygments.min.css b/css/pygments.min.css new file mode 100644 index 0000000..7f125c2 --- /dev/null +++ b/css/pygments.min.css @@ -0,0 +1,184 @@ +.pygments { + /* Comment */ + /* Keyword */ + /* Operator */ + /* Comment.Preproc */ + /* Comment.Special */ + /* Generic.Deleted */ + /* Generic.Emph */ + /* Generic.Error */ + /* Generic.Heading */ + /* Generic.Inserted */ + /* Generic.Output */ + /* Generic.Prompt */ + /* Generic.Strong */ + /* Generic.Subheading */ + /* Generic.Traceback */ + /* Keyword.Declaration */ + /* Keyword.Namespace */ + /* Keyword.Pseudo */ + /* Keyword.Reserved */ + /* Keyword.Type, Keyword.Constant, Name.Class */ + /* Literal.Number */ + /* Literal.String */ + /* Name.Attribute */ + /* Name.Builtin */ + /* Name.Class */ + /* Name.Constant */ + /* Name.Decorator */ + /* Name.Entity */ + /* Name.Exception */ + /* Name.Function */ + /* Name.Label */ + /* Name.Namespace */ + /* Name.Tag */ + /* Name.Variable */ + /* Operator.Word */ + /* Text.Whitespace */ + /* Literal.Number.Float */ + /* Literal.Number.Hex */ + /* Literal.Number.Integer */ + /* Literal.Number.Oct */ + /* Literal.String.Backtick */ + /* Literal.String.Char */ + /* Literal.String.Doc */ + /* Literal.String.Double */ + /* Literal.String.Escape */ + /* Literal.String.Heredoc */ + /* Literal.String.Interpol */ + /* Literal.String.Other */ + /* Literal.String.Regex */ + /* Literal.String.Single */ + /* Literal.String.Symbol */ + /* Name.Builtin.Pseudo */ + /* Name.Variable.Class */ + /* Name.Variable.Global */ + /* Name.Variable.Instance */ + /* Literal.Number.Integer.Long */ } + .pygments .c, .pygments .cm, .pygments .c1 { + color: #9e5555; } + .pygments .k { + color: #6d1717; + font-weight: 400; } + .pygments .o { + color: #666666; } + .pygments .cp { + color: #BC7A00; } + .pygments .cs { + color: #408080; + font-style: italic; } + .pygments .gd { + color: #A00000; } + .pygments .ge { + font-style: italic; } + .pygments .gr { + color: #FF0000; } + .pygments .gh { + color: #000080; + font-weight: 500; } + .pygments .gi { + color: #00A000; } + .pygments .go { + color: #808080; } + .pygments .gp { + color: #000080; + font-weight: 500; } + .pygments .gs { + font-weight: 500; } + .pygments .gu { + color: #800080; + font-weight: 500; } + .pygments .gt { + color: #0040D0; } + .pygments .kd { + color: #ab2626; } + .pygments .kn { + color: #6d1717; + font-weight: 500; } + .pygments .kp { + color: #008000; } + .pygments .kr { + color: #A10327; + font-weight: 500; } + .pygments .kt, .pygments .kc, .pygments .nc { + color: #B00040; } + .pygments .m { + color: #666666; } + .pygments .s { + color: #BA2121; } + .pygments .na { + color: #960037; } + .pygments .nb { + font-weight: 400; } + .pygments .nc { + font-weight: 400; } + .pygments .no { + color: #880000; } + .pygments .nd { + color: #AA22FF; } + .pygments .ni { + color: #999999; + font-weight: 500; } + .pygments .ne { + color: #D2413A; + font-weight: 500; } + .pygments .nf { + color: #960037; } + .pygments .nl { + color: #A0A000; } + .pygments .nn { + color: #6d1717; + font-weight: 500; } + .pygments .nt { + color: #008000; + font-weight: 500; } + .pygments .nv { + color: #1c7805; } + .pygments .ow { + color: #666666; + font-weight: 500; } + .pygments .w { + color: #bbbbbb; } + .pygments .mf { + color: #666666; } + .pygments .mh { + color: #666666; } + .pygments .mi { + color: #666666; } + .pygments .mo { + color: #666666; } + .pygments .sb { + color: #BA2121; } + .pygments .sc { + color: #BA2121; } + .pygments .sd { + color: #BA2121; + font-style: italic; } + .pygments .s2 { + color: #BA2121; } + .pygments .se { + color: #BA2121; + font-weight: 400; } + .pygments .sh { + color: #BA2121; } + .pygments .si { + color: #BA2121; + font-weight: 400; } + .pygments .sx { + color: #008000; } + .pygments .sr { + color: #BB6688; } + .pygments .s1 { + color: #BA2121; } + .pygments .ss { + color: #19177C; } + .pygments .bp { + color: #008000; } + .pygments .vc { + color: #19177C; } + .pygments .vg { + color: #19177C; } + .pygments .vi { + color: #19177C; } + .pygments .il { + color: #666666; } diff --git a/feeds/12factor.atom.xml b/feeds/12factor.atom.xml new file mode 100644 index 0000000..57191e8 --- /dev/null +++ b/feeds/12factor.atom.xml @@ -0,0 +1,41 @@ +Posts tagged ‘12factor’ | Alexis King’s Blog2015-08-30T00:00:00ZManaging application configuration with Envy2015-08-30T00:00:00Z2015-08-30T00:00:00ZAlexis King<article><p>Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, <a href="http://12factor.net">The Twelve-Factor App</a> provides a set of standards for keeping web apps sane, and <a href="http://12factor.net/config">one of those guidelines advises keeping configuration in the environment</a>.</p><p><a href="https://github.com/lexi-lambda/envy">Envy</a> is the declarative bridge between Racket code and the outside world of the environment.</p><h2><a name="introducing-envy"></a>Introducing Envy</h2><p>I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require <code>envy</code> and you're good to go.</p><p>The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:</p><pre><code class="pygments"><span class="c1">; environment.rkt</span> +<span class="kn">#lang </span><span class="nn">typed/racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">envy</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/provide-environment</span> +<span class="w"> </span><span class="n">api-token</span> +<span class="w"> </span><span class="p">[</span><span class="n">log-level</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="kd">#:default</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">info</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">parallel?</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Boolean</span><span class="p">])</span></code></pre><p>When this module is required, Envy will automatically do the following:</p><ol><li><p>Envy will check the values of three environment variables: <code>API_TOKEN</code>, <code>LOG_LEVEL</code>, and <code>PARALLEL</code>.</p></li><li><p>If either <code>API_TOKEN</code> or <code>PARALLEL</code> is not set, an error will be raised:</p><pre><code>envy: The required environment variable "API_TOKEN" is not defined. +</code></pre></li><li><p>The values for <code>LOG_LEVEL</code> and <code>PARALLEL</code> will be parsed to match their type annotations.</p></li><li><p>If <code>LOG_LEVEL</code> is not set, it will use the default value, <code>'info</code>.</p></li><li><p>The values will be stored in <code>api-token</code>, <code>log-level</code>, and <code>parallel?</code>, all of which will be provided by the enclosing module.</p></li></ol><p>Now just <code>(require (prefix-in env: "environment.rkt"))</code>, and the environment variables are guaranteed to be available in your application's code.</p><h2><a name="working-with-typed-racket"></a>Working with Typed Racket</h2><p>As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, <code>define/provide-environment</code> will <em>only</em> work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.</p><p>However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.</p><pre><code>&gt; parallel? +- : Boolean +#t +</code></pre><p>Envy really shines when using optional environment variables with the <code>#:default</code> option. The type of the value given to <code>#:default</code> doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.</p><pre><code>&gt; (define-environment + [num-threads : Positive-Integer #:default #f]) +&gt; num-threads +- : (U Positive-Integer #f) +#f +</code></pre><p>This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.</p><h2><a name="and-more"></a>And more...</h2><p>To see the full set of features that Envy already provides, <a href="https://lexi-lambda.github.io/envy/envy.html">take a look at the documentation</a>. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, <a href="https://github.com/lexi-lambda/envy/issues">open an issue and make a suggestion</a>! I already have plans for a <code>#lang envy</code> DSL, which will hopefully cut the boilerplate out in its entirety.</p><p>And finally, to give credit where credit is due, Envy is heavily inspired by <a href="https://github.com/eval/envied">Envied</a> (both in name and function), an environment variable manager for Ruby, which I've used to great effect.</p><p>Try it out!</p><ul><li><p><code>raco pkg install envy</code></p></li><li><p><a href="https://github.com/lexi-lambda/envy">Envy on GitHub</a></p></li><li><p><a href="https://lexi-lambda.github.io/envy/envy.html">Envy documentation</a></p></li></ul><ol class="footnotes"></ol></article>Deploying Racket applications on Heroku2015-08-22T00:00:00Z2015-08-22T00:00:00ZAlexis King<article><p><a href="https://www.heroku.com">Heroku</a> is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.</p><h2><a name="building-the-server"></a>Building the server</h2><p>Racket's <a href="http://docs.racket-lang.org/web-server/index.html">web-server</a> package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">web-server/servlet</span> +<span class="w"> </span><span class="n">web-server/servlet-env</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="n">req</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">response/xexpr</span> +<span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="ss">html</span><span class="w"> </span><span class="p">(</span><span class="ss">head</span><span class="w"> </span><span class="p">(</span><span class="ss">title</span><span class="w"> </span><span class="s2">"Racket Heroku App"</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="ss">body</span><span class="w"> </span><span class="p">(</span><span class="ss">h1</span><span class="w"> </span><span class="s2">"It works!"</span><span class="p">)))))</span> + +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span></code></pre><p>Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the <code>PORT</code> environment variable. We can access this using the Racket <code>getenv</code>[racket] function.</p><p>Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass <code>#f</code> for the <code>#:listen-ip</code> argument.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;number</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">))</span> +<span class="w"> </span><span class="mi">8080</span><span class="p">))</span> +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span><span class="p">)</span></code></pre><p>Also, by default, <code>serve/servlet</code>[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.</p><pre><code class="pygments"><span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span> +<span class="w"> </span><span class="kd">#:command-line?</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span></code></pre><p>That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.</p><h2><a name="setting-up-our-app-for-heroku"></a>Setting up our app for Heroku</h2><p>The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:</p><pre><code class="pygments">$<span class="w"> </span>git<span class="w"> </span>init +$<span class="w"> </span>heroku<span class="w"> </span>git:remote<span class="w"> </span>-a<span class="w"> </span>racket-heroku-sample +$<span class="w"> </span>heroku<span class="w"> </span>buildpacks:set<span class="w"> </span>https://github.com/lexi-lambda/heroku-buildpack-racket</code></pre><p>We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the <code>RACKET_VERSION</code> environment variable as follows:</p><pre><code class="pygments">$<span class="w"> </span>heroku<span class="w"> </span>config:set<span class="w"> </span><span class="nv">RACKET_VERSION</span><span class="o">=</span><span class="m">6</span>.2.1</code></pre><p>Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.</p><p>Specifically, we just want to run our <code>serve.rkt</code> module. The Racket buildpack installs the repository as a package, so we can run <code>racket</code> with the <code>-l</code> flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:</p><pre><code>web: racket -l sample-heroku-app/server +</code></pre><p>Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can <a href="https://racket-heroku-sample.herokuapp.com">navigate to our app's URL and actually see it running live</a>!</p><h2><a name="conclusion"></a>Conclusion</h2><p>That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.</p><p>That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can <a href="https://github.com/lexi-lambda/racket-sample-heroku-app">find it on GitHub here</a>. For more details on the buildpack itself, <a href="https://github.com/lexi-lambda/heroku-buildpack-racket">it's also available on GitHub here</a>.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/12factor.rss.xml b/feeds/12factor.rss.xml new file mode 100644 index 0000000..b24fa4e --- /dev/null +++ b/feeds/12factor.rss.xml @@ -0,0 +1,41 @@ +Posts tagged ‘12factor’ | Alexis King’s BlogPosts tagged ‘12factor’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/12factor.html30 Aug 201530 Aug 201560Managing application configuration with Envyhttps://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/https://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/30 Aug 2015<article><p>Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, <a href="http://12factor.net">The Twelve-Factor App</a> provides a set of standards for keeping web apps sane, and <a href="http://12factor.net/config">one of those guidelines advises keeping configuration in the environment</a>.</p><p><a href="https://github.com/lexi-lambda/envy">Envy</a> is the declarative bridge between Racket code and the outside world of the environment.</p><h2><a name="introducing-envy"></a>Introducing Envy</h2><p>I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require <code>envy</code> and you're good to go.</p><p>The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:</p><pre><code class="pygments"><span class="c1">; environment.rkt</span> +<span class="kn">#lang </span><span class="nn">typed/racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">envy</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/provide-environment</span> +<span class="w"> </span><span class="n">api-token</span> +<span class="w"> </span><span class="p">[</span><span class="n">log-level</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="kd">#:default</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">info</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">parallel?</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Boolean</span><span class="p">])</span></code></pre><p>When this module is required, Envy will automatically do the following:</p><ol><li><p>Envy will check the values of three environment variables: <code>API_TOKEN</code>, <code>LOG_LEVEL</code>, and <code>PARALLEL</code>.</p></li><li><p>If either <code>API_TOKEN</code> or <code>PARALLEL</code> is not set, an error will be raised:</p><pre><code>envy: The required environment variable "API_TOKEN" is not defined. +</code></pre></li><li><p>The values for <code>LOG_LEVEL</code> and <code>PARALLEL</code> will be parsed to match their type annotations.</p></li><li><p>If <code>LOG_LEVEL</code> is not set, it will use the default value, <code>'info</code>.</p></li><li><p>The values will be stored in <code>api-token</code>, <code>log-level</code>, and <code>parallel?</code>, all of which will be provided by the enclosing module.</p></li></ol><p>Now just <code>(require (prefix-in env: "environment.rkt"))</code>, and the environment variables are guaranteed to be available in your application's code.</p><h2><a name="working-with-typed-racket"></a>Working with Typed Racket</h2><p>As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, <code>define/provide-environment</code> will <em>only</em> work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.</p><p>However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.</p><pre><code>&gt; parallel? +- : Boolean +#t +</code></pre><p>Envy really shines when using optional environment variables with the <code>#:default</code> option. The type of the value given to <code>#:default</code> doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.</p><pre><code>&gt; (define-environment + [num-threads : Positive-Integer #:default #f]) +&gt; num-threads +- : (U Positive-Integer #f) +#f +</code></pre><p>This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.</p><h2><a name="and-more"></a>And more...</h2><p>To see the full set of features that Envy already provides, <a href="https://lexi-lambda.github.io/envy/envy.html">take a look at the documentation</a>. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, <a href="https://github.com/lexi-lambda/envy/issues">open an issue and make a suggestion</a>! I already have plans for a <code>#lang envy</code> DSL, which will hopefully cut the boilerplate out in its entirety.</p><p>And finally, to give credit where credit is due, Envy is heavily inspired by <a href="https://github.com/eval/envied">Envied</a> (both in name and function), an environment variable manager for Ruby, which I've used to great effect.</p><p>Try it out!</p><ul><li><p><code>raco pkg install envy</code></p></li><li><p><a href="https://github.com/lexi-lambda/envy">Envy on GitHub</a></p></li><li><p><a href="https://lexi-lambda.github.io/envy/envy.html">Envy documentation</a></p></li></ul><ol class="footnotes"></ol></article>Deploying Racket applications on Herokuhttps://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/https://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/22 Aug 2015<article><p><a href="https://www.heroku.com">Heroku</a> is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.</p><h2><a name="building-the-server"></a>Building the server</h2><p>Racket's <a href="http://docs.racket-lang.org/web-server/index.html">web-server</a> package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">web-server/servlet</span> +<span class="w"> </span><span class="n">web-server/servlet-env</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="n">req</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">response/xexpr</span> +<span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="ss">html</span><span class="w"> </span><span class="p">(</span><span class="ss">head</span><span class="w"> </span><span class="p">(</span><span class="ss">title</span><span class="w"> </span><span class="s2">"Racket Heroku App"</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="ss">body</span><span class="w"> </span><span class="p">(</span><span class="ss">h1</span><span class="w"> </span><span class="s2">"It works!"</span><span class="p">)))))</span> + +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span></code></pre><p>Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the <code>PORT</code> environment variable. We can access this using the Racket <code>getenv</code>[racket] function.</p><p>Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass <code>#f</code> for the <code>#:listen-ip</code> argument.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;number</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">))</span> +<span class="w"> </span><span class="mi">8080</span><span class="p">))</span> +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span><span class="p">)</span></code></pre><p>Also, by default, <code>serve/servlet</code>[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.</p><pre><code class="pygments"><span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span> +<span class="w"> </span><span class="kd">#:command-line?</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span></code></pre><p>That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.</p><h2><a name="setting-up-our-app-for-heroku"></a>Setting up our app for Heroku</h2><p>The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:</p><pre><code class="pygments">$<span class="w"> </span>git<span class="w"> </span>init +$<span class="w"> </span>heroku<span class="w"> </span>git:remote<span class="w"> </span>-a<span class="w"> </span>racket-heroku-sample +$<span class="w"> </span>heroku<span class="w"> </span>buildpacks:set<span class="w"> </span>https://github.com/lexi-lambda/heroku-buildpack-racket</code></pre><p>We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the <code>RACKET_VERSION</code> environment variable as follows:</p><pre><code class="pygments">$<span class="w"> </span>heroku<span class="w"> </span>config:set<span class="w"> </span><span class="nv">RACKET_VERSION</span><span class="o">=</span><span class="m">6</span>.2.1</code></pre><p>Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.</p><p>Specifically, we just want to run our <code>serve.rkt</code> module. The Racket buildpack installs the repository as a package, so we can run <code>racket</code> with the <code>-l</code> flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:</p><pre><code>web: racket -l sample-heroku-app/server +</code></pre><p>Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can <a href="https://racket-heroku-sample.herokuapp.com">navigate to our app's URL and actually see it running live</a>!</p><h2><a name="conclusion"></a>Conclusion</h2><p>That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.</p><p>That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can <a href="https://github.com/lexi-lambda/racket-sample-heroku-app">find it on GitHub here</a>. For more details on the buildpack itself, <a href="https://github.com/lexi-lambda/heroku-buildpack-racket">it's also available on GitHub here</a>.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/all.atom.xml b/feeds/all.atom.xml new file mode 100644 index 0000000..088167b --- /dev/null +++ b/feeds/all.atom.xml @@ -0,0 +1,3172 @@ +Alexis King’s Blog2025-05-29T00:00:00ZA break from programming languages2025-05-29T00:00:00Z2025-05-29T00:00:00ZAlexis King<article><p>This is a blog post I have been considering writing for a long time.</p><p>People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.</p><p>My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.</p><p>This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for <em>me</em>, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why <em>not</em>—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.</p><h2><a name="whence-programming-languages"></a>Whence programming languages</h2><p>I have had a personal fascination with programming languages for as long as I have been using them. I made my first serious forays into programming in the fifth grade, writing (very simple and mostly very bad) Flash games in ActionScript 3. I wrote my first programming language interpreter in my sophomore year of high school, a dynamically-typed, object-oriented Lisp with prototypal inheritance. The interpreter was written in C, and despite it being my first serious endeavor with the language, I used preprocessor macros to implement a form of exceptions in terms of <code>setjmp.h</code>. It is an amusing project in retrospect, given what my design sensibilities would eventually turn out to be, but it at least provides a little insight into how much the subject was already on my mind in my teenage years.</p><p>I have always deeply enjoyed programming. That is no less true now than it was fifteen years ago. As someone with (then untreated) ADHD, the automation of repetitive tasks has always held immediate appeal, but it did not take long to discover I enjoy programming for its own sake. It is inherently delightful to me to direct an infinitely reconfigurable machine towards any end I desire, given only a little thought and some time at a keyboard. Program <em>design</em>, even in the large, was something that immediately appealed to me. It is often as much a challenge of organizing one’s thoughts as it is a matter of translating them to a form the computer can understand, and the former is a challenge I have always found rewarding.</p><p>Even so, as every programmer inevitably discovers, the gap between one’s thoughts and executable code is never so narrow as we would like. I quickly discovered that a distressingly large amount of my time was being spent on the exactly the sort of repetitive task I had hoped to avoid, mechanically typing out reams of boilerplate in order to carry out what seemed to be incredibly simple tasks.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> It felt strange that we, as programmers, could be one of the lucky disciplines equipped with the means to produce our own tools, yet the tools we work with are so frequently unsatisfactory.</p><p>Programming is an astonishingly young discipline. FORTRAN, arguably the first recognizable programming language, is less than seventy years old, and the first stored-program computers are only a decade older. Programmers have been trying to bridge the gap between our conceptualizations of programs and the (often laborious) act of programming for as long as computing has existed. As John Backus said in 1979,</p><blockquote><p>Much of my work has come from being lazy. I didn’t like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs.</p></blockquote><p>In my experience, this sentiment perfectly captures the strange relationship many programming language enthusiasts have with our craft. We enjoy programming so much that we are willing to spend enormous time, thought, and effort working on programming systems precisely to free ourselves from the tortuous burden of writing programs.</p><p>One possible means of reconciling this apparent contradiction would be to hypothesize that programmers like me or like Backus enjoy the <em>results</em> of our programs but not the process of writing them. We want to get things done, and we view the computer as a useful but necessary evil that should get out of the way as much and as expediently as possible. However, although I am sure there are many programmers who do match that description, I do not count myself among them, and I would likewise find it extremely difficult to believe that Backus—a man who dedicated his life to designing ever more sophisticated means of expressing computer programs—was someone fundamentally uninterested in programming.</p><p>On the contrary, I see a great deal of work on programming languages as a struggle—often in vain—by those of us who love programming so much that we wish to free it of its unfortunate imperfections. We strive to remove the impediments and obstacles we face when translating the beautiful, platonic programs we hold in our heads into the comparatively cold, alien language of silicon calculating machines. There is a widespread mythology that programming languages are primarily abstractions over machine code, but this perspective has been misguided for almost as long as programming languages have existed. Programming languages are instruments of thought, frameworks for precisely specifying an executable idea, and they are designed almost entirely for <em>humans</em>, with computers often providing little more than a loose set of inconvenient restrictions.</p><h3><a name="to-compute-is-human-to-program-divine"></a>To compute is human, to program divine</h3><p>The earliest computers were developed with a singular purpose in mind: to compute. A program was, essentially, a sequence of instructions containing the computations to be performed. This conceptualization of a computer program as essentially analogous to a cooking recipe or assembly manual has remained curiously persistent, even to the present day.</p><p>Almost no modern programs of any complexity look anything like a cooking recipe. Even programs written in the most imperative programming languages under the sun are complex webs of logic specifying dataflow, access policy, and interaction patterns between semi-autonomous, reactive subsystems, many of which come equipped with their own living, beating hearts. At the micro scale, it is true that programs specify behavior as a series of steps to be performed, but even this is a fiction maintained by modern compilers for humans’ convenience. Through a compiler’s eyes, the <em>real</em> programs are whatever is left after the comforting fiction is boiled away to obtain a homogenous pile of SSA and dataflow graphs to be haphazardly reassembled into a patchwork of basic blocks in a language most programmers would not even begin to understand.</p><p>Indeed, in stark contrast to the mental model of the programmer as a sort of silicon whisperer, I believe most practicing software engineers are primarily maintaining vast behavioral specifications whose defining sense of rigor comes not from computers <em>per se</em> but from the consistency we demand from their results. In other words, we are trained to be fluent speakers of languages that allow us to articulate a solution to a problem with a level of precision that ensures every legitimate interpretation is and will always be to our satisfaction. Crucially, these specifications do not <em>fully</em> specify how the task is to be carried out. Optimizing compilers are free to make their own choices with whatever leeway we permit them. HTML/CSS user agents may interpret our instructions in whatever way preserves whatever web standards happen to require. Even RDBMS query planners, a foundational technology largely unchanged for the past several decades, are essentially expert systems designed to decide on the fly how best to execute searches and retrievals under ever-changing conditions.</p><p>This is not to say that the task of programming is fully removed from the machine that ultimately executes our instructions, nor is it even really a suggestion it ought to be. Performance and debugging mandate a certain familiarity with the underlying system, even if the full complexities of its true inner workings remain carefully hidden from even its expert users. My point is simply that programs are not rote algorithms, and the act of programming is not primarily characterized by algorithmic thinking. Rather, programming is fundamentally a game of domain modeling and system design, with bits of algorithmic logic inserted here and there to glue the whole thing together.</p><p>My interest in pushing the frontier of programming language design has always been and continues to be an interest in making the task of writing programs feel closer to the task of designing them. In an ideal world, I would have a language expressive enough to more or less directly translate the computer programs I have in my head (which, for me, more often than not take <em>visual</em> form) into a digitized structure a computer may faithfully execute. It is that task I found myself fixated on at the tender age of fifteen, and it is one I would remain fixated on for the better part of the following decade.</p><h2><a name="ten-years-of-programming-languages"></a>Ten years of programming languages</h2><p>I turned twenty eight this year. Almost exactly ten years ago, I started my first professional software engineering job. It was a fairly mundane position writing Ruby on Rails and JavaScript, but even then, I spent a great deal of free time pursuing the programming language hobby projects that truly caught my fancy. My first professional experience working with Haskell would come less than a year later, experience that would soon inspire my hobby research project to implement a Haskell-style type system within the Racket macro system. The majority of my career to date has followed more or less directly from that formative time.</p><p>After a decade, it seems fair to reflect a little on what I have learned, what I managed to achieve, and maybe more significantly, what I did <em>not</em>. Something one learns very quickly upon beginning any serious work on programming languages is that they are startlingly, unforgivably <em>hard</em>. A vast array of dizzyingly smart people have been working on the problem of program specification for seventy years; there are not many pieces of low hanging fruit left on the tree, and any new gains tend to be very hard won.</p><p>Still, even if programming language design is challenging, it would be some consolation if we were able to slowly yet steadily advance the state of the art, gradually picking away at the gap between the tools we have now and the ultimate programming language of the future. Sadly, even that pragmatic vision does not survive contact with reality. In practice, the challenge of programming language design is not one of expanding a well-defined frontier, it is grappling with a neverending list of fundamental <em>tradeoffs</em> between mutually incompatible features.</p><p>Subtyping is tantalizingly useful but makes complete type inference incredibly difficult (and in general, provably impossible). Structural typing drastically reduces the burden of assigning a <em>name</em> to every uninteresting intermediate form but more or less precludes taking advantage of Haskell-style typeclasses or Rust-style traits. Dynamic dispatch substantially assists decoupling of software components but can come with a significant performance cost without a JIT. Just-in-time compilation can use runtime information to optimize code in ways that permit more flexible coding patterns, but JIT performance can be unpredictable, and the overhead of an optimizing JIT is substantial. Sophisticated metaprogramming systems can radically cut down on boilerplate and improve program concision and even readability, but they can have substantial runtime or compile-time overhead and tend to thwart automated tooling. The list goes on and on.</p><p>The essential, most stubborn problems in programming languages come from unavoidable tensions between conflicting desires and requirements. We want loosely coupled software components that can be easily reused, but we also want the performance benefits of tight coupling and specialization. We want flexible programming languages that do not impose upon our freedom of expression, but we also want the benefits of static program analysis and powerful safety guarantees. We want sophisticated type systems that allow specifying ever more complex invariants, but we also want readable type signatures that won’t regularly end up longer than the code itself.</p><p>We cannot build the One True Programming Language because we cannot have everything at once, and we cannot hope to universally decide which tradeoffs are the right ones to make. This naturally makes the thought of a system constructed from a tapestry of many different programming languages attractive, affording the programmer the opportunity to select the tool best suited to the task at hand. Unfortunately, that, too, comes with (often brutal) tradeoffs of its own: the impedance mismatch at language boundaries, the vastly more complex tooling required for a polyglot system, and the increased knowledge burden of maintaining a system built in so many different ways.</p><p>Ten years of working on and thinking about programming languages has forced me to come to terms with a humbling truth: I do not know how to build a better programming language. I am personally extremely sympathetic to the idea that the future of programming probably involves more domain-specific languages, and I think the industry as a whole has been (very slowly) trending in that direction for quite some time. But even I acknowledge that the question of how precisely that ought to be done is extraordinarily complicated, and there are no easy answers here.</p><p>Even so, it would be far too dismal of me to suggest that the problem is hopeless, nor do I believe the work does not matter. Even if the problem has proven more difficult than teenage me may have hoped, the work <em>is</em> satisfying when it bears fruit. Unfortunately, even after toiling for years overcoming daunting problems, every working programming language enthusiast is forced to confront a much more frustrating enemy: <em>programmers</em>.</p><h3><a name="the-reactionary-conservatism-of-the-median-programmer"></a>The reactionary conservatism of the median programmer</h3><p>It should come as no surprise that programming language design is largely a social problem. Choice of programming language tends to be driven by strong network effects, and programmers tend to prefer languages that resemble what they already know. Programmers’ general lack of curiosity and outright hostility to learning new things has always been a personal frustration of mine, but I ultimately do understand much of where it comes from: as engineers, an overwhelming part of our job description is managing <em>risk</em>. Exotic technology choices are risky, and it is a responsible impulse to be wary of them.</p><p>Still, the history of mainstream programming languages is essentially a story of programmers vocally and emphatically rejecting what eventually proved to be some of the most incredibly successful innovations in the history of the field. Assembly programmers largely laughed at FORTRAN, but just a few decades later, there were nevertheless very few remaining assembly programmers. First-class functions were widely derided as needlessly complicated and confusing until programmers were forced to finally take the time to learn to use them once JavaScript became a load-bearing language by historical accident, and within a decade, they became a required feature for every major programming system. Sophisticated type systems largely retain a perception of overengineered, ivory-tower elitism, but many of the programmers who hold those very opinions have enthusiastically adopted Rust, a language that features a type system so complex that idiomatic Rust code can easily put Haskell programs to shame.</p><p>Discussions of programming languages are, by and large, emotionally driven shouting matches based on anecdotes and gut feelings. Surprisingly (or perhaps not), although most working programming language theorists and compiler engineers have a far more informed perspective on this subject (and do at least tend to refrain from petty debate), disparaging remarks directed towards languages not to the speaker’s taste are hardly unheard of even at academic conferences dedicated to the subject. I want to be very clear that I do not think that is somehow indicative of some toxicity within the community—they are by and large a group of truly amazing, wonderful people, and such comments are usually delivered with tongue planted firmly in cheek. All the same, my point is simply that programming languages are an emotional, opinionated subject in a way that seems perhaps inherent to the craft, <a href="/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/">a topic I explored at some length all the way back in 2019.</a></p><p>I do earnestly believe that the field of software engineering would benefit from more openness to new ideas and willingness to experiment with new technologies. I also think it would be enormously refreshing if programmers did not nearly universally speak with an expert’s confidence on subjects they have only passingly encountered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> Nevertheless, I do not expect programmers’ tribalism to diminish anytime soon, so the pragmatist in me understands their nature imposes real limitations on what we as a field can meaningfully accomplish.</p><p>In my experience, most working programming language researchers have essentially resigned themselves to this fact, and the prevailing wisdom is to almost entirely decouple the value of the work from its impact. Even if an idea is broadly ignored by programmers today, if it’s a good enough idea, it will still make its way into some programming language in some distant tomorrow, as many ideas eventually have. Lexical closures were first applied to programming languages in 1975 but took a full thirty years to be broadly accepted. Algebraic datatypes were first introduced around the same time and have only recently found mainstream acceptance through Rust. Innovations take a long, <em>long</em> time to make the jump from research to practice in this field, and placing one’s sense of professional accomplishment in the fickle hands of mainstream programmers’ whims and preferences is a recipe for personal misery.</p><p>I have nothing but respect for this attitude, genuinely. This research community is astonishingly friendly, warm, creative, fun, and accepting, especially given the vitriol these subjects tend to elicit from programmers at large. I feel genuinely honored to have had the opportunity to personally collaborate with many of them, and the collective output of endlessly fascinating ideas and exciting projects one can find each and every year at every major PL conference is consistently inspiring. All the same, as much respect as I have for the people who work tirelessly on these problems for their own sake, I am not sure I can continue to count myself among them. I do find the subject exciting, and I do consider it rewarding in its own way, but these past few years, I have slowly been coming to terms with the fact that I don’t think I find it quite rewarding enough.</p><h2><a name="why-i-write-computer-programs"></a>Why I write computer programs</h2><p>I like writing software. It is strange how radical a statement that sometimes feels. So many programmers seem to practically despise what they do for a living, at least as you’d hear them describe it. I have never really been able to empathize with that mindset. I find programming fun, and I derive a unique joy from writing code that decisively solves the problem at hand. It doesn’t have to be a particularly interesting problem, and the solution doesn’t even have to require much more than snapping blocks together, but as long as I can take some pride and satisfaction in the craftsmanship and end result, I am usually pretty happy.</p><p>My attraction to libraries, tooling, and compilers has always stemmed from a mixture of personal motivation and a certain satisfaction from improving things for my peers. One of my favorite things about software engineering is taking a problem and solving it in such a way that it largely <em>stays solved</em>. Sure, it may need to change as requirements evolve, but the solution continues to work even after the labor is done. Other tasks in my life do not provide nearly the same sense of accomplishment. When I do a load of laundry, it provides me with clean clothes for a week or two, but ultimately, I know it won’t be long before I’m going to have to do it again. Software engineering is not like that. Working on software—even extremely mundane software—provides a pleasant variety of tasks that can be well and truly <em>completed</em>. I like that sense of progress, and I appreciate the relaxing, almost meditative process of growing and maintaining the metaphorical garden that is a collection of code.</p><p>Given all of the above, it is not altogether clear why I would struggle to take satisfaction in my work on programming languages. If anything, working on a compiler seems like the ultimate realization of my aforementioned desires applied to the very thing I, myself, spend my day doing. If there is some annoying or repetitive task, and I can extend my programming system to provide a better way of doing it, that task is something I will never again need to do. What’s more, working on a compiler that thousands of people actually use means this is not just benefiting me, it’s benefiting everyone else as well. It feels good to have that kind of impact, and to know that my effort is essentially multiplied by everyone who happens to use it.</p><p>On paper, this ought to make working on a compiler like GHC enormously satisfying. Even if I feel frustrated by mainstream attitudes towards programming languages, Haskell programmers feel differently. Surely, working on something used and appreciated by Haskellers ought to feel very rewarding. But it wasn’t really. There were parts that were nice, yes, and the problems were often interesting. But I didn’t find it <em>rewarding</em> in the way I had hoped, and I’m not sure it even compared favorably to working on bog-standard software. Why?</p><h3><a name="on-the-haskell-community"></a>On the Haskell community</h3><p>This was the hardest section of this blog post to write. It is easy to write about the things I love and the people I admire. It is perhaps even easier to write about the things I despise and the people I disdain. The Haskell community is neither. I do not <em>dislike</em> it. Several of its members are people I consider very good friends. Nonetheless, I have never managed to fall in any particular love with it, and even if that is no sin on its part, it is a somewhat sad thing to have to confess.</p><p>I want to be <em>excruciatingly</em> clear that I do not wish to mount any indictment of the Haskell community or any of the people in it. Many people seem to have a preconception of Haskellers as mean and elitist, and while I do get the sense there have been some historically bad actors that contributed to that perception, my general impression is that those people have been thoroughly expunged. I have never been the target of any memorably rude conduct, and I’ve definitely never felt targeted by any prejudice or discrimination, and if anything I have always felt universally warmly welcomed at every meetup or conference I have attended. I cannot speak for anyone but myself, and my experiences are necessarily limited. I cannot hope to claim with any certainty that no abuse has ever occurred or that it does not continue to occur within any Haskell’s myriad social spaces, most of which I have never participated in. Still, I am happy to be able to say that I have never personally experienced any real unfriendliness, and that is certainly not a factor in my dispassion for the language’s community. If anything, I feel I must extend a very genuine <em>thank you</em> for all the lovely support I have received from Haskellers over the years, and it is that support and enthusiasm that so often kept me motivated for as long as I was.</p><p>This is all simply to say that I do not think it is the <em>fault</em> of anyone in particular for my lack of affinity for the Haskell community. Rather, I think perhaps it is just not a culture that I have ever especially related to. That probably does not even say very much, as I’m not sure there are many communities I <em>could</em> say that about. That says much more about me than it does about Haskell or Haskellers.</p><p>It is admittedly tempting to say that perhaps I find these communities alienating in part because they lack people I identify with and relate to. After all, it is simply true that the Haskell community is <em>overwhelmingly</em> male, which really has felt lonely at times. It’s also true that the Haskell community tends to attract a lot of people who are mainly interested in Haskell because they find it intellectually interesting, or because they like category theory and enjoy libraries like <code>lens</code> that have explored those ideas, while I am really only interested in Haskell insofar as it is a practical vehicle for writing useful software. Nevertheless, there <em>are</em> other women who write Haskell and there <em>are</em> other people who care very much about writing useful software in Haskell, and that doesn’t seem to have radically altered my relationship to the community, so I think it would be disingenuous of me to claim that either of those flaws are substantially to blame.</p><p>If there is one substantive frustration I can express with Haskell, it is where the money comes from. For as long as I have been involved with it, Haskell has predominantly been used and funded by a combination of fintech and cryptocurrency. I don’t really have anything against fintech, though I certainly don’t find it especially exciting, but I do have something against crypto, and those who followed me on Twitter before its untimely demise know I have always been pretty open about that fact. My thoughts on the matter are really not something I want to elaborate on here, but suffice to say it is not especially motivating to work on a language if all of its users are applying it in ways I feel essentially indifferent about at best and actively hostile to at worst. This is obviously not something I have any easy answers to, but it is probably the most significant, concrete factor in me losing my interest in working on GHC, so if you mourn me leaving, at least I can give you one semi-actionable reason.</p><p>Make no mistake: GHC is a truly incredible project that often still feels like a compiler from the future, and having the opportunity to work on it as a part of my job was about as technologically stimulating as I could possibly ask for. Even so, during my time at Tweag, I often found myself reflecting on the fact that the <em>users</em> I was working for were, ultimately, Haskell programmers, a class I’m not sure even really contains <em>me</em>. I have often reflected on the fact that, although I have many personal projects, I have <em>never</em> chosen Haskell for any of them. Not once. Whenever I need to write a little code to accomplish some task, it’s always Racket. This blog is in Racket. My personal shell utilities are in Racket. When I want to scrape a website or wrap some library, I do it with Racket. Haskell is a language I have always exclusively written professionally, and although I do very earnestly love many things about it, it is not so precious to me that I feel motivated to contribute to it out of passion alone. If there were more <em>users</em> of Haskell doing inspiring, exciting things with the tools I was working on, I might feel substantially more driven to indirectly contribute to those causes. But Haskell is a niche language used by a select few, and it is hard for me to spend so much of my life working on a technological marvel that practically nobody will ever benefit from.</p><p>As a final note on this subject: I do believe very earnestly that the functional programming and programming languages communities would benefit enormously from more demographic diversity. Even compared to software engineering as a whole, the gender ratio is, frankly, almost unbelievably grim. I have always been drawn to interests and hobbies that tend to skew male, and that has never really bothered me, so if <em>I</em> have found myself feeling alienated by the conspicuous absence of other women—to the point that, in the past, I have sometimes considered giving up on Haskell primarily for that reason—I don’t think it is outrageous to say things are pretty dire. A wider set of perspectives could, in theory, inject some refreshing creativity into the dreary ecosystem that is industrial Haskell. Of course, I will admit that I have absolutely no idea how that end could possibly be achieved, and it is a subject far broader and more complicated than I think belongs in this blog post. For now, I will leave things at that, but I hope my voice is sufficiently well regarded that some readers may take my comments to heart.</p><h2><a name="reflections-on-a-decade-s-work"></a>Reflections on a decade’s work</h2><p>Before writing this blog post, I essentially pitched it to a group of friends as “a sort of apology”. At times I certainly feel burdened by the weight of all the endeavors I’ve left unfinished, all the work I never carried long enough to bear fruit. Even so, as I write these words, I find myself reflecting on the things I feel I <em>did</em> manage to contribute these past ten years, and it strikes me that the trail I’ve left behind is perhaps not so dismal, after all.</p><p>It is comforting to look back on a decade’s accumulations and realize there is enough to take some real pride in. I am proud of my contributions to Racket and Haskell, and of my numerous libraries in each of them. I am proud of my experiments, among them <a href="https://github.com/lexi-lambda/hackett">Hackett</a> and <a href="https://github.com/lexi-lambda/eff">eff</a>, and the excellent work from others they have inspired. I am proud of this very blog, from the <a href="/blog/2019/11/05/parse-don-t-validate/">most impactful</a> things I’ve written to the <a href="/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/">far more niche</a>. I am proud of my <a href="https://langdev.stackexchange.com/users/861/alexis-king?tab=answers&amp;sort=newest">contributions to Programming Languages Stack Exchange</a>, a site I most certainly intend to remain a moderator of and will undoubtedly continue to contribute to. And I am proud of the <a href="https://www.youtube.com/watch?v=0jI-AlWEwYI">numerous talks</a> I have <a href="https://www.youtube.com/watch?v=TE48LsgVlIU">presented over the years</a>, all of them labors of love.</p><p>I have not always accomplished all the things I set out to achieve. Some have, for some reason or another, gotten the better of me. Many of them, from my <a href="https://github.com/lexi-lambda/blackboard">math typesetting system</a> to my <a href="https://github.com/ghc-proposals/ghc-proposals/pull/303">improvements to GHC’s arrow notation</a>, are things I hope to one day push over the finish line. But for now it is clear my heart is no longer really in them, so I think it is time for me to let them go. Perhaps others will find inspiration in them. Perhaps you will succeed where I did not.</p><p>It is a little sad to think about moving on from such a substantial chapter of my life. To date, I have spent almost my entire career in this professional niche, and I suspect I will never fully leave it. It has been a pleasure to work with so many bright, lovely, inspiring people, and perhaps one day I will find myself eager to return once more. But for now, my interests are elsewhere, and despite the bittersweet feelings, there is also a very real excitement in the tantalizing opportunity to do something new.</p><h2><a name="what-next"></a>What next?</h2><p>It is a little amusing to think that what I find myself most drawn to after a decade of pursuing ever more exotic projects is something entirely mundane: I want to write utterly ordinary software. I want to work on a piece of software used by people to accomplish a task, and I want to take pride and satisfaction in doing it well.</p><p>I do not yet really have any idea what that might look like. It has been over six years since I last worked on anything that really feels like it could possibly fit that description, and even then, it was only one part of my job. Is it so strange that after spending fifteen years of my life trying everything I could to get away from spending all day wrangling a big ball of mud in a Java IDE, that experience sounds pretty cozy to return to?</p><p>In late 2022, I decided on a whim to reverse engineer and resurrect a 2008 Java browser game called <a href="https://github.com/lexi-lambda/shattered-plans">Shattered Plans</a>. While doing so, I had the opportunity to spend an awful lot of time in IntelliJ, and after years of acclimatizing to writing code in fanciful languages with nothing more than a plain text editor, a terminal emulator, and a dream, the experience was almost viscerally thrilling. They say the grass is always greener, but even if that is true, I think I’d like to find it out for myself.</p><p>More to the point, my life is just very different now from how it was for most of my twenties. I was single, and it was easy to justify spending almost all of my time thinking about code, whether on the clock or not. It was hardly unheard of for me to spend twelve or even sixteen hours at my desk, just tinkering on something that had captured my attention. It was fun, and I’m glad I did it, but I am also very ready to work a simple, regular job I don’t feel the need to permanently devote a large portion of my soul to. It isn’t very romantic, but then, I have other things for that.</p><p>If you think you have a position that meets those (admittedly extremely broad) criteria, and you think you might like to hire me, by all means: <a href="mailto:lexi.lambda@gmail.com">send me an email</a>. The worst I can do is not reply. And who knows? With the burden of my own expectations behind me, perhaps I’ll even blow the dust off this old blog of mine and start writing things again. As with my job, whatever I might choose to write next I cannot say. Either way, it’s a little exciting to be able to treat myself to a blank slate.</p><p>I could probably spend twenty thousand words writing in circles about all my thoughts and feelings on this subject. However, to be honest, I don’t especially want to. As this blog post has hopefully communicated, I would like to be done, and so this will have to be enough. Allow me to personally thank all of you who have indulged me by reading my disorganized ramblings to the end.</p><p>It’s been a long time. Here’s to another ten years.</p><ol class="footnotes"><li id="footnote-1"><p>Readers may find it clarifying to note that a great deal of my early programming experience was writing (then era-appropriate) Java 6. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I have often wondered if other engineering disciplines have anything to rival the collective overconfidence of the average Hacker News commenter. This is an earnest curiosity, not merely a rhetorical flourish; I do sometimes wonder what it is about my field of choice that engenders such disregard for the value of expertise. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>An introduction to typeclass metaprogramming2021-03-25T00:00:00Z2021-03-25T00:00:00ZAlexis King<article><p><em>Typeclass metaprogramming</em> is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem), and it is the core mechanism used to implement generic programming via <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">GHC generics</a>. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.</p><p>This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does <em>not</em> attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also <em>not</em> a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.</p><h2><a name="part-1-basic-building-blocks"></a>Part 1: Basic building blocks</h2><p>Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.</p><p>To start, we’ll cover the absolute foundations of typeclass metaprogramming.</p><h3><a name="typeclasses-as-functions-from-types-to-terms"></a>Typeclasses as functions from types to terms</h3><p>As its name implies, typeclass metaprogramming (henceforth TMP<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup>) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic <code>==</code> operator via the <code>Eq</code> class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: <strong>typeclasses are functions from types to (runtime) terms</strong>.</p><p>What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called <code>TypeOf</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Given these instances, we can observe that they do what we expect in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>Note that both the <code>TypeOf Bool</code> and <code>TypeOf Char</code> instances ignore the argument to <code>typeOf</code> altogether. This makes sense, as the whole point of the <code>TypeOf</code> class is to get access to <em>type</em> information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This typeclass definition is a little unusual, as the type parameter <code>a</code> doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>the full type of the <code>show</code> method is implicitly extended with a <code>Show a</code> constraint to yield:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Furthermore, if we write <code>forall</code>s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the <em>full</em> type of <code>show</code>:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>In the same vein, we can write out the full type of <code>typeOf</code>, as given by our new definition of <code>TypeOf</code>:</p><pre><code class="pygments"><span class="nf">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This type is still unusual, as the <code>a</code> type parameter doesn’t appear anywhere to the right of the <code>=&gt;</code> arrow. This makes the type parameter trivially <em>ambiguous</em>, which is to say it’s impossible for GHC to infer what <code>a</code> should be at any call site. Fortunately, <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html">we can use <code>TypeApplications</code></a> to pass a type for <code>a</code> directly, as we can see in the updated definition of <code>TypeOf (a, b)</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Once again, we can test out our new definitions in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="kt">Bool</span> +<span class="s">"Bool"</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Bool</span><span class="p">,</span><span class="w"> </span><span class="kt">Char</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our <code>typeOf</code> function is, quite literally, a function that accepts a single type as an argument and returns a term-level <code>String</code>. Of course, the <code>TypeOf</code> typeclass is not a particularly <em>useful</em> example of such a function, but it demonstrates how easy it is to construct.</p><h3><a name="type-level-interpreters"></a>Type-level interpreters</h3><p>One important consequence of eliminating the value-level argument of <code>typeOf</code> is that there is no need for its argument type to actually be <em>inhabited</em>. For example, consider the <code>TypeOf</code> instance on <code>Void</code> from <code>Data.Void</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Void</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Void"</span></code></pre><p>This above instance is no different from the ones on <code>Bool</code> and <code>Char</code> even though <code>Void</code> is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of <code>TypeOf</code> on lists:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"["</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"]"</span></code></pre><p>If <code>typeOf</code> required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type <code>a</code> to recursively apply <code>typeOf</code> to. But since <code>typeOf</code> only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.</p><p>A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Z</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="n">a</span></code></pre><p>It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:</p><ul><li><p><code>Z</code> is a type that represents 0.</p></li><li><p><code>S Z</code> is a type that represents 1.</p></li><li><p><code>S (S Z)</code> is a type that represents 2.</p></li></ul><p>And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Numeric.Natural</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>As its name implies, <code>reifyNat</code> reifies a type-level natural number encoded using our datatypes above into a term-level <code>Natural</code> value:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="kt">Z</span> +<span class="mi">0</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span> +<span class="mi">1</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="mi">2</span></code></pre><p>One way to think about <code>reifyNat</code> is as an <em>interpreter</em> of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.</p><h3><a name="overlapping-instances"></a>Overlapping instances</h3><p>Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for <code>Show (Maybe a)</code>, you aren’t supposed to <em>also</em> write an instance for <code>Show (Maybe Bool)</code>, since it isn’t clear whether <code>show (Just True)</code> should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.</p><p>Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.</p><p>As a simple example, suppose we wanted to write a typeclass that checks whether a given type is <code>()</code> or not:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:</p><pre><code class="pygments"><span class="c1">-- not actually valid Haskell, just an example</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>But if we try to translate this to typeclass instances, we’ll get a problem:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> This means GHC will complain about instance overlap if we try to evaluate <code>isUnit @()</code>:</p><pre><code>ghci&gt; isUnit @() + +error: + • Overlapping instances for IsUnit () + arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance IsUnit () +</code></pre><p>To fix this, we have to explicitly mark <code>IsUnit ()</code> as overlapping:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>Now GHC accepts the expression without complaint:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="nb">()</span> +<span class="kt">True</span></code></pre><p>What does the <code>{-# OVERLAPPING #-}</code> pragma do, exactly? The gory details are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances">spelled out in the GHC User’s Guide</a>, but the simple explanation is that <code>{-# OVERLAPPING #-}</code> relaxes the overlap checker as long as the instance is <em>strictly more specific</em> than the instance(s) it overlaps with. In this case, that is true: <code>IsUnit ()</code> is trivially more specific than <code>IsUnit a</code>, since the former only matches <code>()</code> while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.</p><p>Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>The intent of <code>guardUnit</code> is to use <code>isUnit</code> to detect if its argument is of type <code>()</code>, and if it is, to return an error. However, even though we marked <code>IsUnit ()</code> overlapping, we still get an overlapping instance error:</p><pre><code>error: + • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance [overlapping] IsUnit () + • In the expression: isUnit @a +</code></pre><p>What gives? The problem is that GHC simply doesn’t know what type <code>a</code> is when compiling <code>guardUnit</code>. It <em>could</em> be instantiated to <code>()</code> where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.</p><p>This behavior is actually a very, very good thing. If GHC were to blindly pick the <code>IsUnit a</code> instance in this case, then <code>guardUnit</code> would always take the <code>False</code> branch, even when passed a value of type <code>()</code>! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when <code>{-# OVERLAPPING #-}</code> annotations are used, so it’s important to keep their limitations in mind.</p><p>As it happens, in this particular case, the error is easily remedied. We simply have to add an <code>IsUnit</code> constraint to the type signature of <code>guardUnit</code>:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now picking the right <code>IsUnit</code> instance is deferred to the place where <code>guardUnit</code> is used, and the definition is accepted.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><h3><a name="type-families-are-functions-from-types-to-types"></a>Type families are functions from types to types</h3><p>In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE TypeFamilies #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>The above is a <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families">closed type family</a>, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of <code>Sum</code> would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="nf">sum</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span> +<span class="nf">sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="nf">sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="n">sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.</p><p>To test our definition of <code>Sum</code> in GHCi, we can use <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind">the <code>:kind!</code> command</a>, which prints out a type and its kind after reducing it as much as possible:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span></code></pre><p>We can also combine <code>Sum</code> with our <code>ReifyNat</code> class from earlier:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.</p><h3><a name="example-1-generalized-concat"></a>Example 1: Generalized <code>concat</code></h3><p>Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a <code>flatten</code> function similar to like-named functions provided by many dynamically-typed languages. In those languages, <code>flatten</code> is like <code>concat</code>, but it works on a list of arbitrary depth. For example, we might use it like this:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]</span></code></pre><p>In Haskell, lists of different depths have different types, so multiple levels of <code>concat</code> have to be applied explicitly. But using TMP, we can write a generic <code>flatten</code> function that operates on lists of any depth!</p><p>Since this is <em>typeclass</em> metaprogramming, we’ll unsurprisingly begin with a typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="o">???</span><span class="p">]</span></code></pre><p>Our first challenge is writing the return type of <code>flatten</code>. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>Now we can write our <code>Flatten</code> instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>The inductive case is when the type is a nested list, in which case we want to apply <code>concat</code> and recur:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">(</span><span class="n">concat</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Sadly, if we try to compile these definitions, GHC will reject our <code>Flatten [a]</code> instance:</p><pre><code>error: + • Couldn't match type ‘a’ with ‘ElementOf [a]’ + ‘a’ is a rigid type variable bound by + the instance declaration + Expected type: [ElementOf [a]] + Actual type: [a] + • In the expression: x + In an equation for ‘flatten’: flatten x = x + In the instance declaration for ‘Flatten [a]’ + | + | flatten x = x + | ^ +</code></pre><p>At first blush, this error looks very confusing. Why doesn’t GHC think <code>a</code> and <code>ElementOf [a]</code> are the same type? Well, consider what would happen if we picked a type like <code>[Int]</code> for <code>a</code>. Then <code>[a]</code> would be <code>[[Int]]</code>, a nested list, so the first case of <code>ElementOf</code> would apply. Therefore, GHC refuses to pick the second equation of <code>ElementOf</code> so hastily.</p><p>In this particular case, we might think that’s rather silly. After all, if <code>a</code> were <code>[Int]</code>, then GHC wouldn’t have picked the <code>Flatten [a]</code> instance to begin with, it would pick the more specific <code>Flatten [[a]]</code> instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.</p><p>Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our <code>Flatten [a]</code> instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>This is a <em>type equality constraint</em>. Type equality constraints are written with the syntax <code>a ~ b</code>, and they state that <code>a</code> must be the same type as <code>b</code>. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that <code>ElementOf [a]</code> must always be <code>a</code>, which allows the instance to typecheck.</p><p>Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must <em>eventually</em> be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the <code>Flatten [a]</code> instance is selected, GHC will know that <code>a</code> is <em>not</em> a list type, and it will be able to reduce <code>ElementOf [a]</code> to <code>a</code> without difficulty. Indeed, we can see this for ourselves by using <code>flatten</code> in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span></code></pre><p>It works! But why do we need the type annotation on <code>1</code>? If we leave it out, we get a rather hairy type error:</p><pre><code>error: + • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ + Expected type: [ElementOf [a]] + Actual type: [ElementOf [a0]] + NB: ‘ElementOf’ is a non-injective type family + The type variable ‘a0’ is ambiguous +</code></pre><p>The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a <code>Num [a]</code> instance, in which case <code>1</code> could actually have a list type, and either case of <code>ElementOf</code> could match depending on the choice of <code>Num</code> instance. Of course, no such <code>Num</code> instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.</p><p>This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call <code>flatten</code> on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.</p><p>That wrinkle aside, <code>flatten</code> is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.</p><h4><a name="typeclasses-as-compile-time-code-generation"></a>Typeclasses as compile-time code generation</h4><p>Presented with the above definition of <code>Flatten</code>, it might not be immediately obvious how to think about <code>Flatten</code> as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, <code>Eq</code> or <code>Show</code>) than the <code>TypeOf</code> and <code>ReifyNat</code> classes we defined above.</p><p>One useful way to shift our perspective is to consider equivalent <code>Flatten</code> instances written using point-free style:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">id</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">concat</span></code></pre><p>These definitions of <code>flatten</code> no longer (syntactically) depend on term-level arguments, just like our definitions of <code>typeOf</code> and <code>reifyNat</code> didn’t accept any term-level arguments above. This allows us to consider what <code>flatten</code> might “expand to” given a type argument alone:</p><ul><li><p><code>flatten @[Int]</code> is just <code>id</code>, since the <code>Flatten [a]</code> instance is selected.</p></li><li><p><code>flatten @[[Int]]</code> is <code>flatten @[Int] . concat</code>, since the <code>Flatten [[a]]</code> instance is selected. That then becomes <code>id . concat</code>, which can be further simplified to just <code>concat</code>.</p></li><li><p><code>flatten @[[[Int]]]</code> is <code>flatten @[[Int]] . concat</code>, which simplifies to <code>concat . concat</code> by the same reasoning above.</p></li><li><p><code>flatten @[[[[Int]]]]</code> is then <code>concat . concat . concat</code>, and so on.</p></li></ul><p>This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of <code>flatten</code> takes a type as an argument and produces some number of composed <code>concat</code>s as a result. From this perspective, <code>Flatten</code> is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.</p><p>This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name <em>metaprogramming</em>. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.</p><h2><a name="part-2-generic-programming"></a>Part 2: Generic programming</h2><p>Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.</p><p>In the previous section, we discussed how to use TMP to write a generic <code>flatten</code> operation. In this section, we’ll aim a bit higher: totally generic functions that operate on <em>arbitrary</em> datatypes.</p><h3><a name="open-type-families-and-associated-types"></a>Open type families and associated types</h3><p>Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, <em>open type families</em>. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using <code>type instance</code> declarations. For example, we could define our <code>Sum</code> family from above like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>In the case of <code>Sum</code>, this would not be very useful, and indeed, <code>Sum</code> is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.</p><p>This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a <code>Key</code> open type family that relates types to the types used to index them:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span></code></pre><p>This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>In this case, anyone could define their own data structure, define instances of <code>Key</code> and <code>HasKey</code> for their data structure, and use <code>hasKey</code> to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>An open family declared inside a typeclass like this is called an <em>associated type</em>. It works exactly the same way as the separate definitions of <code>Key</code> and <code>HasKey</code>, it just uses a different syntax. Note that although the <code>family</code> and <code>instance</code> keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).</p><p>Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like <a href="https://hackage.haskell.org/package/mono-traversable"><code>mono-traversable</code></a> are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.</p><p>However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes <em>heavy</em> use of open type families: datatype-generic programming.</p><h3><a name="example-2-datatype-generic-programming"></a>Example 2: Datatype-generic programming</h3><p><em>Datatype-generic programming</em> refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include</p><ul><li><p>equality, comparison, and hashing,</p></li><li><p>recursive traversal of self-similar data structures, and</p></li><li><p>serialization and deserialization,</p></li></ul><p>among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on <em>any</em> datatype.</p><p>In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.</p><h4><a name="generic-datatype-representations"></a>Generic datatype representations</h4><p>At the heart of the <code>Generic</code> class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Authentication</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AuthBasic</span><span class="w"> </span><span class="kt">Username</span><span class="w"> </span><span class="kt">Password</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AuthSSH</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>then we have a type that is essentially equivalent to this one:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>If we know how to define a function on a nested tree built out of <code>Either</code>s and pairs, then we know how to define it on <em>any</em> such datatype! This is where TMP comes in: recall the way we viewed <code>Flatten</code> as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?</p><p>The answer to that question is <em>yes</em>. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, <code>numFields (AuthBasic "alyssa" "pass1234")</code> would return <code>2</code>, while <code>numFields (AuthSSH "&lt;key&gt;")</code> would return <code>1</code>. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.</p><p>We’ll start by using TMP to implement a “generic” version of <code>numFields</code> that operates on trees of <code>Either</code>s and pairs as described above:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="c1">-- base case: leaf value</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>Just like our <code>Flatten</code> class from earlier, <code>GNumFields</code> uses the type-level structure of its argument to choose what to do:</p><ul><li><p>If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.</p></li><li><p>If we find <code>Left</code> or <code>Right</code>, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.</p></li><li><p>In the case of any other value, we’re at a “leaf” in the tree of <code>Either</code>s and pairs, which corresponds to a single field, so we just return <code>1</code>.</p></li></ul><p>Now if we call <code>gnumFields (Left ("alyssa", "pass1234"))</code>, we’ll get <code>2</code>, and if we call <code>gnumFields (Right "&lt;key&gt;")</code>, we’ll get <code>1</code>. All that’s left to do is write a bit of code that converts our <code>Authentication</code> type to a tree of <code>Either</code>s and pairs:</p><pre><code class="pygments"><span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericizeAuthentication</span></code></pre><p>Now we get the results we want on our <code>Authentication</code> type using <code>numFieldsAuthentication</code>, but we’re not done yet, since it only works on <code>Authentication</code> values. Is there a way to define a generic <code>numFields</code> function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFields</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericize</span></code></pre><p>Now <code>numFields (AuthBasic "alyssa" "pass1234")</code> returns <code>2</code>, as desired, and it will <em>also</em> work with any datatype that provides a <code>Generic</code> instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:</p><ul><li><p>First, we define the <code>Generic</code> class, comprised of two parts:</p><ol><li><p>The <code>Rep a</code> associated type maps a type <code>a</code> onto its generic, sums-of-products representation, i.e. one built out of combinations of <code>Either</code> and pairs.</p></li><li><p>The <code>genericize</code> method converts an actual <em>value</em> of type <code>a</code> to the equivalent value using the sums-of-products representation.</p></li></ol></li><li><p>Next, we define a <code>Generic</code> instance for <code>Authentication</code>. <code>Rep Authentication</code> is the sums-of-products representation we described above, and <code>genericize</code> is likewise <code>genericizeAuthentication</code> from above.</p></li><li><p>Finally, we define <code>numFields</code> as a function with a <code>GNumFields (Rep a)</code> constraint. This is where all the magic happens:</p><ul><li><p>When we apply <code>numFields</code> to a datatype, <code>Rep</code> retrieves its generic, sums-of-products representation type.</p></li><li><p>The <code>GNumFields</code> class then uses various TMP techniques we’ve already described so far in this blog post to generate a <code>numFields</code> implementation on the fly from the structure of <code>Rep a</code>.</p></li><li><p>Finally, that generated <code>numFields</code> implementation is applied to the genericized term-level value, and the result is produced.</p></li></ul></li></ul><p>After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) <code>numFields</code> operation. Surely just defining <code>numFields</code> on each type directly would be far easier? Indeed, if we were just considering <code>numFields</code>, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined <code>numFields</code>, and all of them would automatically work on <code>Authentication</code> because they all leverage the same <code>Generic</code> instance!</p><p>This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation <em>once</em>, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.</p><h4><a name="improving-our-definition-of-generic"></a>Improving our definition of <code>Generic</code></h4><p>You may note that the definition of <code>Generic</code> provided above does not match the one in <code>GHC.Generic</code>. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a <code>GHC.Generics</code> tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.</p><h5><a name="distinguishing-leaves-from-the-spine"></a>Distinguishing leaves from the spine</h5><p>One problem with our version of <code>Generic</code> is that it provides no way to distinguish an <code>Either</code> or pair that should be considered a “leaf”, as in a type like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">A</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">B</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">)</span></code></pre><p>Given this type, <code>Rep Foo</code> should be <code>Either (Either Int String) (Char, Bool)</code>, and <code>numFields (Right ('a', True))</code> will erroneously return <code>2</code> rather than <code>1</code>. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">getLeaf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Now our <code>Generic</code> instances look like this:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">PublicKey</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">key</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">))</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">A</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">B</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Since the <code>Leaf</code> constructor now distinguishes a leaf, rather than the absence of an <code>Either</code> or <code>(,)</code> constructor, we’ll have to update our <code>GNumFields</code> instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.</p><h5><a name="handling-empty-constructors"></a>Handling empty constructors</h5><p>Suppose we have a type with nullary data constructors, like the standard <code>Bool</code> type:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>How do we write a <code>Generic</code> instance for <code>Bool</code>? Using just <code>Either</code>, <code>(,)</code>, and <code>Leaf</code>, we can’t, but if we are willing to add a case for <code>()</code>, we can use it to denote nullary constructors:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="nb">()</span></code></pre><p>In a similar vein, we could use <code>Void</code> to represent datatypes that don’t have any constructors at all.</p><h4><a name="continuing-from-here"></a>Continuing from here</h4><p>The full version of <code>Generic</code> has a variety of further improvements useful for generic programming, including:</p><ul><li><p>Support for converting from <code>Rep a</code> to <code>a</code>.</p></li><li><p>Special indication of self-recursive datatypes, making generic tree traversals possible.</p></li><li><p>Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.</p></li><li><p>Fully automatic generation of <code>Generic</code> instances via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric">the <code>DeriveGeneric</code> extension</a>, which reduces the per-type boilerplate to essentially nothing.</p></li></ul><p>The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.</p><h2><a name="part-3-dependent-typing"></a>Part 3: Dependent typing</h2><p>It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.</p><h3><a name="datatype-promotion"></a>Datatype promotion</h3><p>In part 1, we used uninhabited datatypes like <code>Z</code> and <code>S a</code> to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, <code>True</code> and <code>False</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">True</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">False</span></code></pre><p>Now we could define type families to provide operations on these types, such as <code>Not</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>However, this has some frustrating downsides:</p><ul><li><p>First, it’s simply inconvenient that we have to define these new <code>True</code> and <code>False</code> “dummy” types, which are completely distinct from the <code>Bool</code> type provided by the prelude.</p></li><li><p>More significantly, it means <code>Not</code> has a very unhelpful kind:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span></code></pre><p>Even though <code>Not</code> is only <em>supposed</em> to be applied to <code>True</code> or <code>False</code>, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like <code>Not Char</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span> +<span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span></code></pre><p>Rather than getting an error, GHC simply spits <code>Not Char</code> back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.</p></li></ul><p>One way to think about <code>Not</code> is that it is largely <em>dynamically kinded</em> in the same way some languages are dynamically typed. That isn’t entirely true, as we technically <em>will</em> get a kind error if we try to apply <code>Not</code> to a type constructor rather than a type, such as <code>Maybe</code>:</p><pre><code>ghci&gt; :kind! Not Maybe + +&lt;interactive&gt;:1:5: error: + • Expecting one more argument to ‘Maybe’ + Expected a type, but ‘Maybe’ has kind ‘* -&gt; *’ +</code></pre><p>…but <code>*</code> is still a very big kind, much bigger than we would like to permit for <code>Not</code>.</p><p>To help with both these problems, GHC provides <em>datatype promotion</em> via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html">the <code>DataKinds</code> language extension</a>. The idea is that for each normal, non-GADT type definition like</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>then in addition to the normal type constructor and value constructors, GHC also defines several <em>promoted</em> constructors:</p><ul><li><p><code>Bool</code> is allowed as both a type and a kind.</p></li><li><p><code>'True</code> and <code>'False</code> are defined as new types of kind <code>Bool</code>.</p></li></ul><p>We can see this in action if we remove our <code>data True</code> and <code>data False</code> declarations and adjust our definition of <code>Not</code> to use promoted constructors:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DataKinds #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;True</span></code></pre><p>Now the inferred kind of <code>Not</code> is no longer <code>* -&gt; *</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>Consequently, we will now get a kind error if we attempt to apply <code>Not</code> to anything other than <code>'True</code> or <code>'False</code>:</p><pre><code>ghci&gt; :kind! Not Char + +&lt;interactive&gt;:1:5: error: + • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ +</code></pre><p>This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">&#39;Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">&#39;S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>Note that we need to add an explicit kind signature on the definition of the <code>ReifyNat</code> typeclass, since otherwise GHC will assume <code>a</code> has kind <code>*</code>, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that <code>Z</code> and <code>S</code> are related, this prevents someone from coming along and defining a nonsensical instance like <code>ReifyNat Char</code>, which previously would have been allowed but will now be rejected with a kind error.</p><p>Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.</p><h3><a name="gadts-and-proof-terms"></a>GADTs and proof terms</h3><p>So far in this blog post, we have discussed several different function-like things:</p><ul><li><p>Ordinary Haskell functions are functions from terms to terms.</p></li><li><p>Type families are functions from types to types.</p></li><li><p>Typeclasses are functions from types to terms.</p></li></ul><p>A curious reader may wonder about the existence of a fourth class of function:</p><ul><li><p><em>???</em> are functions from terms to types.</p></li></ul><p>To reason about what could go in the <em>???</em> above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?</p><p>In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.</p><p>GADTs<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup> are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html">described in detail in the GHC User’s Guide</a>, but the key idea for our purposes is that <em>pattern-matching on a GADT constructor can refine type information</em>. Here’s a simple, silly example:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Bool</span> +<span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Int</span> + +<span class="nf">doSomething</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">x</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span></code></pre><p>Here, <code>WhatIsIt</code> is a datatype with two nullary constructors, <code>ABool</code> and <code>AnInt</code>, similar to a normal, non-GADT datatype like this one:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AnInt</span></code></pre><p>What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, <code>ABool</code> and <code>AnInt</code> would both have the type <code>forall a. WhatIsIt a</code>, but in the GADT definition, we explicitly fix <code>a</code> to <code>Bool</code> in the type of <code>ABool</code> and to <code>Int</code> in the type of <code>AnInt</code>.</p><p>This simple feature allows us to do very interesting things. The <code>doSomething</code> function is polymorphic in <code>a</code>, but on the right-hand side of the first equation, <code>x</code> has type <code>Bool</code>, while on the right-hand side of the second equation, <code>x</code> has type <code>Int</code>. This is because the <code>WhatIsIt a</code> argument effectively constrains the type of <code>a</code>, as we can see by experimenting with <code>doSomething</code> in GHCi:</p><pre><code>ghci&gt; doSomething ABool True +False +ghci&gt; doSomething AnInt 10 +11 +ghci&gt; doSomething AnInt True + +error: + • Couldn't match expected type ‘Int’ with actual type ‘Bool’ + • In the second argument of ‘doSomething’, namely ‘True’ + In the expression: doSomething AnInt True + In an equation for ‘it’: it = doSomething AnInt True +</code></pre><p>One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The <code>ABool</code> constructor is a proof of <code>a ~ Bool</code>, while the <code>AnInt</code> constructor is a proof of <code>a ~ Int</code>. When you construct <code>ABool</code> or <code>AnInt</code>, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.</p><p>GADTs can be much more sophisticated than our simple <code>WhatIsIt</code> type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:</p><pre><code class="pygments"><span class="kr">infixr</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span> + +<span class="kr">data</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">HCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This type is a <em>heterogenous list</em>, a list that can contain elements of different types:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">t</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Num</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[Bool, [Char]</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>An <code>HList</code> is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total <code>head</code> function on <code>HList</code>s like this:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Remarkably, GHC does not complain that this definition of <code>head</code> is non-exhaustive. Since we specified that the argument must be of type <code>HList (a ': as)</code> in the type signature for <code>head</code>, GHC knows that the argument <em>cannot</em> be <code>HNil</code> (which would have the type <code>HList '[]</code>), so it doesn’t ask us to handle that case.</p><p>These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.</p><h4><a name="proofs-that-work-together"></a>Proofs that work together</h4><p>This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an <code>HList</code> of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with <code>head</code>, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.</p><p>However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a <em>proof term</em> that contains no values, it just encapsulates type equalities on a type-level list:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a]</span> +<span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b]</span> +<span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b, c]</span></code></pre><p>We call it a proof term because a value of type <code>OneToThree a b c as</code> constitutes a <em>proof</em> that <code>as</code> has exactly 1, 2, or 3 elements. Using <code>OneToThree</code>, we can write a function that accepts an <code>HList</code> accompanied by a proof term:</p><pre><code class="pygments"><span class="nf">sumUpToThree</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">z</span></code></pre><p>As with <code>head</code>, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:</p><ul><li><p>When we match on the <code>OneToThree</code> proof term, information flows from the term level to the type level, refining the type of <code>as</code> in that branch.</p></li><li><p>The refined type of <code>as</code> then flows back down to the term level, restricting the shape the <code>HList</code> can take and refinine the set of patterns we have to match.</p></li></ul><p>Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an <code>HList</code> has an even number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This is a proof which itself has inductive structure: <code>EvenCons</code> takes a proof that <code>as</code> has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an <code>HList</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span> + +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Once again, this definition is completely exhaustive, and we can show that it works in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="kt">EvenNil</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.</p><h4><a name="proof-inference"></a>Proof inference</h4><p>While our definition of <code>pairUp</code> is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the <code>Even</code> proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.</p><p>Remember that typeclasses are functions from types to terms. As its happens, a value of type <code>Even as</code> can be mechanically produced from the structure of the type <code>as</code>. This suggests that we could use TMP to automatically generate <code>Even</code> proofs, and indeed, we can. In fact, it’s not at all complicated:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>We can now adjust our <code>pairUp</code> function to use <code>IsEven</code> instead of an explicit <code>Even</code> argument:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>This is essentially identical to its old definition, but by acquiring the proof via <code>IsEven</code> rather than passing it explicitly, we can call <code>pairUp</code> without having to construct a proof manually:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This is rather remarkable. Using TMP, we are able to get GHC to <em>automatically construct a proof that a list is even</em>, with no programmer guidance beyond writing the <code>IsEven</code> typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: <code>IsEven</code> is a function that accepts a type-level list and generates an <code>Even</code> proof term.</p><p>From this perspective, <strong>typeclasses are a way of specifying a proof search algorithm</strong> to the compiler. In the case of <code>IsEven</code>, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.</p><h3><a name="aside-gadts-versus-type-families"></a>Aside: GADTs versus type families</h3><p>Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.</p><p>Consider again the type of the <code>pairUp</code> function above (without the typeclass for simplicity):</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>We used both a GADT, <code>Even</code>, and a type family, <code>PairUp</code>. But we could have, in theory, used <em>only</em> a GADT and eliminated the type family altogether. Consider this variation on the <code>Even</code> proof term:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="p">((</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span></code></pre><p>This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of <code>as</code> as an “input” parameter and <code>bs</code> as an “output” parameter. The idea is that any <code>EvenPairs</code> proof relates both an even-length list type and its paired up equivalent:</p><ul><li><p><code>EvenNil</code> has type <code>EvenPairs '[] '[]</code>,</p></li><li><p><code>EvenCons EvenNil</code> has type <code>EvenPairs '[a, b] '[(a, b)]</code>,</p></li><li><p><code>EvenCons (EvenCons EvenNil)</code> has type <code>EvenPairs '[a, b, c, d] '[(a, b), (c, d)]</code>,</p></li><li><p>…and so on.</p></li></ul><p>This allows us to reformulate our <code>pairUp</code> type signature this way:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">bs</span></code></pre><p>The definition is otherwise unchanged. The <code>PairUp</code> type family is completely gone, because now <code>EvenPairs</code> itself defines the relation. In this way, GADTs can be used like type-level functions!</p><p>The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Kind</span><span class="w"> </span><span class="p">(</span><span class="kt">Constraint</span><span class="p">)</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span></code></pre><p>The idea here is that <code>IsEvenTF as</code> produces a constraint can only be satisfied if <code>as</code> has an even number of elements, since that’s the only way it will eventually reduce to <code>()</code>, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting <code>IsEvenTF as =&gt;</code> in a type signature successfully restricts <code>as</code> to be an even-length list, but it doesn’t allow us to write <code>pairUp</code>. To see why, we can try the following definition:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Unlike the version using the GADT, this version of <code>pairUp</code> is not considered exhaustive:</p><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil +</code></pre><p>This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by <code>IsEvenTF</code> provides no term-level evidence about the shape of <code>as</code>, so we can’t branch on it the way we can branch on the <code>Even</code> GADT.<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> (In a sense, <code>IsEvenTF</code> is doing <a href="/blog/2019/11/05/parse-don-t-validate/">validation, not parsing</a>.)</p><p>For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.</p><h3><a name="guiding-type-inference"></a>Guiding type inference</h3><p>So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.</p><p>However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.</p><p>To illustrate what that can look like, suppose we want to use TMP to generate an <code>HList</code> full of <code>()</code> values of an arbitrary length:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Testing in GHCi, we can see it behaves as desired:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[(), (), ()]</span> +<span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>Now suppose we write a function that accepts a list containing exactly one element and returns it:</p><pre><code class="pygments"><span class="nf">unsingleton</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[a]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unsingleton</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Naturally, we would expect these to compose without a hitch. If we write <code>unsingleton unitList</code>, our TMP should generate a list of length 1, and we should get back <code>()</code>. However, it may surprise you to learn that <em>isn’t</em>, in fact, what happens:<sup><a href="#footnote-6" id="footnote-ref-6-1">6</a></sup></p><pre><code>ghci&gt; unsingleton unitList + +error: + • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ + prevents the constraint ‘(UnitList '[a0])’ from being solved. + Probable fix: use a type annotation to specify what ‘a0’ should be. + These potential instances exist: + instance UnitList as =&gt; UnitList (() : as) +</code></pre><p>What went wrong? The type error says that <code>a0</code> is ambiguous, but it only lists a single matching <code>UnitList</code> instance—the one we want—so how can it be ambiguous which one to select?</p><p>The problem stems from the way we defined <code>UnitList</code>. When we wrote the instance</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span></code></pre><p>we said the first element of the type-level list must be <code>()</code>, so there’s nothing stopping someone from coming along and defining another instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="kt">Int</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>In that case, GHC would have no way to know which instance to pick. Nothing in the type of <code>unsingleton</code> forces the element in the list to have type <code>()</code>, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.</p><p>Of course, this isn’t what we want. The <code>UnitList</code> class is supposed to <em>always</em> return a list of <code>()</code> values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Here we’ve changed the instance so that it has the shape <code>UnitList (a ': as)</code>, with a type variable in place of the <code>()</code>, but we also added an equality constraint that forces <code>a</code> to be <code>()</code>. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unsingleton</span><span class="w"> </span><span class="n">unitList</span> +<span class="nb">()</span></code></pre><p>To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="o">&lt;</span><span class="n">constraints</span><span class="o">&gt;</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">C</span><span class="w"> </span><span class="o">&lt;</span><span class="n">types</span><span class="o">&gt;</span></code></pre><p>The part to the left of the <code>=&gt;</code> is known as the <em>instance context</em>, while the part to the right is known as the <em>instance head</em>. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, <strong>only the instance head matters, and the instance context is completely ignored</strong>. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.</p><p>This explains why our two <code>UnitList</code> instances behave differently:</p><ul><li><p>Given the instance head <code>UnitList (() ': as)</code>, GHC won’t select the instance unless it knows the first element of the list is <code>()</code>.</p></li><li><p>But given the instance head <code>UnitList (a ': as)</code>, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.</p></li></ul><p>After the <code>UnitList (a ': as)</code> instance is selected, GHC attempts to solve the constraints in the instance context, including the <code>a ~ ()</code> constraint. This <em>forces</em> <code>a</code> to be <code>()</code>, resolving the ambiguity and allowing type inference to proceed.</p><p>This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:</p><ul><li><p>If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.</p></li><li><p>But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell <em>you</em> what type this is,” effectively giving you a role in type inference itself.</p></li></ul><p>From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.</p><p>Given all of the above, consider again the definition of <code>IsEven</code> from earlier:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an <code>IsEven (a ': b0)</code> constraint, where <code>b0</code> is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an <code>IsEven '[a]</code> instance in the future.</p><p>To fix this, we can apply the same trick we used for <code>UnitList</code>, just in a slightly different way:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Again, the idea is to move the type information we <em>learn</em> from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can <strong>dramatically</strong> improve type inference in programs that make heavy use of TMP.</p><h3><a name="example-3-subtyping-constraints"></a>Example 3: Subtyping constraints</h3><p>At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at <a href="https://hasura.io/">Hasura</a>, I had the opportunity to design an internal parser combinator library that captures aspects of the <a href="https://graphql.org/">GraphQL</a> type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.</p><p>Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our <code>GQLKind</code> datatype has three cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLKind</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Both</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Input</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Output</span></code></pre><p>We use <code>DataKind</code>-promoted versions of this <code>GQLKind</code> type as a parameter to a <code>GQLType</code> GADT:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">TScalar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Both</span> +<span class="w"> </span><span class="kt">TInputObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">InputObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Input</span> +<span class="w"> </span><span class="kt">TIObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Output</span> +<span class="w"> </span><span class="c1">-- ...and so on...</span></code></pre><p>This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type <code>GQLType 'Input</code>, we can’t pass a <code>GQLType 'Both</code>, even though we really ought to be able to.</p><p>To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span></code></pre><p>The first case, <code>KRefl</code>, states that every kind is trivially a subkind of itself. The second case, <code>KBoth</code>, states that <code>Both</code> is a subkind of any kind at all. (This is a particularly literal example of <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">using a type to define axioms</a>.) The next step is to use TMP to implement proof inference:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KBoth</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span></code></pre><p>These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that <code>k</code> is a superkind of <code>'Input</code> or <code>'Output</code>, type inference will force them to be equal.</p><p>Using <code>IsSubKind</code>, we can easily resolve the problem described above. Rather than write a function with a type like this:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>…we simply use an <code>IsSubKind</code> constraint, instead:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now both <code>'Input</code> and <code>'Both</code> kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. <em>Consuming</em> the <code>SubKind</code> proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SelectionSet</span></code></pre><p>This type family is used to determine what a <code>GQLParser k a</code> actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>.</p><p>Fortunately, that is very easy to do using <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html">the <code>(:~:)</code> type from <code>Data.Type.Equality</code> in <code>base</code></a> to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">a</span></code></pre><p>Just as with any other GADT, <code>(:~:)</code> can be used to pack up type equalities and unpack them later; <code>a :~: b</code> just happens to be the GADT that corresponds precisely to the equality <code>a ~ b</code>. Using <code>(:~:)</code>, we can write a reusable proof that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>:</p><pre><code class="pygments"><span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="o">@</span><span class="kt">&#39;Input</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span></code></pre><p>This function is a very simple proof by cases, where <code>Refl</code> can be read as “Q.E.D.”:</p><ul><li><p>In the first case, matching on <code>KRefl</code> refines <code>k</code> to <code>'Input</code>, and <code>ParserInput 'Input</code> is <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li><li><p>Likewise, in the second case, matching on <code>KBoth</code> refines <code>k</code> to <code>'Both</code>, and <code>ParserInput 'Both</code> is also <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li></ul><p>This <code>inputParserInput</code> helper allows functions like <code>nullable</code>, which internally need <code>ParserInput k ~ InputValue</code>, to take the form</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nullable</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">inputParserInput</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ...implementation goes here... -}</span></code></pre><p>Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without <code>IsSubKind</code> doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!</p><h2><a name="wrapping-up-and-closing-thoughts"></a>Wrapping up and closing thoughts</h2><p>So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:</p><ul><li><p>Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.</p></li><li><p>Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.</p></li><li><p>Though I’ve called the technique “<em>typeclass</em> metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.</p></li><li><p>Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.</p></li></ul><p>The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.</p><p>Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.</p><p>It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:</p><ul><li><p>As mentioned earlier in this blog post, <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">the <code>GHC.Generics</code> module documentation</a> is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.</p></li><li><p>I have long believed that <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/">the GHC User’s Guide</a> is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.</p></li><li><p>Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the <a href="https://hackage.haskell.org/package/singletons">singletons</a> library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, <a href="https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf">the accompanying paper</a> is definitely worth a read if you’d like to go down that route.)</p></li></ul><p>Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.</p><ol class="footnotes"><li id="footnote-1"><p>Not to be confused with C++’s <a href="https://en.wikipedia.org/wiki/Template_metaprogramming"><em>template</em> metaprogramming</a>, though there are significant similarities between the two techniques. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>There have been proposals to introduce ordered instances, known in the literature as <a href="https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf"><em>instance chains</em></a>, but as of this writing, GHC does not implement them. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Note that this also preserves an important property of the Haskell type system, parametricity. A function like <code>id :: a -&gt; a</code> shouldn’t be allowed to do different things depending on which type is chosen for <code>a</code>, which our first version of <code>guardUnit</code> tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Short for <em>generalized algebraic datatypes</em>, which is a rather unhelpful name for actually understanding what they are or what they’re for. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for <code>IsEvenTF</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">exists</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">as&#39;</span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">)</span></code></pre><p>The type refinement provided by matching on <code>HCons</code> would be enough for the second case of <code>IsEvenTF</code> to be selected, which would provide an equality proof that <code>as</code> has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. <a href="#footnote-ref-5-1">↩</a></p></li><li id="footnote-6"><p>Actually, I’ve cheated a little bit here, because <code>unsingleton unitList</code> really does typecheck in GHCi under normal circumstances. That’s because <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules">the <code>ExtendedDefaultRules</code> extension</a> is enabled in GHCi by default, which defaults ambiguous type variables to <code>()</code>, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned <code>ExtendedDefaultRules</code> off to illustrate the problem. <a href="#footnote-ref-6-1">↩</a></p></li></ol></article>Names are not type safety2020-11-01T00:00:00Z2020-11-01T00:00:00ZAlexis King<article><p>Haskell programmers spend a lot of time talking about <em>type safety</em>. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published <a href="/blog/2019/11/05/parse-don-t-validate/">Parse, Don’t Validate</a> as an initial stab towards bridging that gap.</p><p>The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s <code>newtype</code> construct. The idea is simple enough—the <code>newtype</code> keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this <em>sounds</em> like a simple and straightforward path to type safety. For example, one might consider using a <code>newtype</code> declaration to define a type for an email address:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This technique can provide <em>some</em> value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct <em>kind</em> of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.</p><p>And names are not type safety.</p><h2><a name="intrinsic-and-extrinsic-safety"></a>Intrinsic and extrinsic safety</h2><p>To illustrate the difference between constructive data modeling (discussed at length in my <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">previous blog post</a>) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">One</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Two</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Three</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Four</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Five</span></code></pre><p>We could then write some functions to convert between <code>Int</code> and our <code>OneToFive</code> type:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">One</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Two</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Three</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Four</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Five</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">3</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">5</span></code></pre><p>This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="kt">Int</span></code></pre><p>Just as before, we can provide <code>toOneToFive</code> and <code>fromOneToFive</code> functions, with identical types:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">otherwise</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="p">(</span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">n</span></code></pre><p>If we put these declarations in their own module and choose not to export the <code>OneToFive</code> constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.</p><p>To see why, suppose we write a function that consumes a <code>OneToFive</code> value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"first"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"second"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"third"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fifth"</span></code></pre><p>The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an <code>Int</code>—after all, it <em>is</em> an <code>Int</code>. An <code>Int</code> can of course contain many other values besides <code>1</code> through <code>5</code>, so we are forced to add an error case to satisfy the exhaustiveness checker:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromOneToFive</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"first"</span> +<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"second"</span> +<span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"third"</span> +<span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fifth"</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: bad OneToFive value"</span></code></pre><p>In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:</p><ul><li><p>The constructive datatype captures its invariants in such a way that they are <em>accessible</em> to downstream consumers. This frees our <code>ordinal</code> function from worrying about handling illegal values, as they have been made unutterable.</p></li><li><p>The newtype wrapper provides a smart constructor that <em>validates</em> the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting <code>Int</code>s.</p></li></ul><p>Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of <code>error</code> has punched a hole right through our type system. If we were to add another constructor to our <code>OneToFive</code> datatype,<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> the version of <code>ordinal</code> that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.</p><p>All of this is a consequence of the fact that the constructive modeling is <em>intrinsically</em> type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent <code>6</code> using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an <code>Int</code>; its meaning is specified extrinsically via the <code>toOneToFive</code> smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.</p><h3><a name="revisiting-non-empty-lists"></a>Revisiting non-empty lists</h3><p>Our <code>OneToFive</code> datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the <code>NonEmpty</code> datatype I’ve repeatedly highlighted in recent blog posts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>It may be illustrative to imagine a version of <code>NonEmpty</code> represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Foldable</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Just as with <code>OneToFive</code>, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for <code>NonEmpty</code> was the ability to write a safe version of <code>head</code>, but the newtype version requires another assertion:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">x</span> +<span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines <code>NonEmpty</code>, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.</p><h2><a name="newtypes-as-tokens"></a>Newtypes as tokens</h2><p>If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes <em>can</em> provide a sort of safety, just a weaker one.</p><p>The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a <em>trust boundary</em> where internal invariants are enforced by restricting clients to a safe API.</p><p>We can use the <code>NonEmpty</code> example from above to illustrate how this works. We refrain from exporting the <code>NonEmpty</code> constructor, and we provide <code>head</code> and <code>tail</code> operations that we trust to never actually fail:</p><pre><code class="pygments"><span class="kr">module</span><span class="w"> </span><span class="nn">Data.List.NonEmpty.Newtype</span> +<span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">NonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">cons</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">nonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">head</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">tail</span> +<span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">cons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span> +<span class="nf">cons</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span> + +<span class="nf">tail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>Since the only way to construct or consume <code>NonEmpty</code> values is to use the functions in <code>Data.List.NonEmpty.Newtype</code>’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like <em>tokens</em>: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case <code>head</code> and <code>tail</code>, to obtain the values contained within.</p><p>This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid <code>NonEmpty []</code> value. For this reason, the newtype approach to type safety does not on its own constitute a <em>proof</em> that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a <code>Generic</code> instance for <code>NonEmpty</code>:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DeriveGeneric #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">GHC.Generics</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span></code></pre><p>However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">GHC</span><span class="o">.</span><span class="kt">Generics</span><span class="o">.</span><span class="n">to</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">K1</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>This is a particularly extreme example, since derived <code>Generic</code> instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived <code>Read</code> instance:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">read</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="s">"NonEmpty []"</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:</p><ul><li><p>All invariants must be made clear to maintainers of the trusted module. For simple types, such as <code>NonEmpty</code>, the invariant is self-evident, but for more sophisticated types, comments are not optional.</p></li><li><p>Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.</p></li><li><p>Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.</p></li><li><p>Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.</p></li></ul><p>In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.</p><p>In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.</p><h2><a name="other-newtype-use-abuse-and-misuse"></a>Other newtype use, abuse, and misuse</h2><p>The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:</p><ul><li><p>Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the <code>Sum</code> and <code>Product</code> newtypes from <code>Data.Monoid</code> provide useful <code>Monoid</code> instances for numeric types.</p></li><li><p>In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The <code>Flip</code> newtype from <code>Data.Bifunctor.Flip</code> is a simple example, flipping the arguments of a <code>Bifunctor</code> so the <code>Functor</code> instance may operate on the other side:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runFlip</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.</p></li><li><p>More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a <code>ByteString</code> containing a secret key may be wrapped in a newtype (with a <code>Show</code> instance omitted) to discourage code from accidentally logging or otherwise exposing it.</p></li></ul><p>All of these applications are good ones, but they have little to do with <em>type safety.</em> The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually <em>prevents</em> misuse; any part of the program may inspect the value at any time.</p><p>Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">unArgumentName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSONKey</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSONKey</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">Hashable</span><span class="p">,</span><span class="w"> </span><span class="kt">ToTxt</span><span class="p">,</span><span class="w"> </span><span class="kt">Lift</span><span class="p">,</span><span class="w"> </span><span class="kt">Generic</span><span class="p">,</span><span class="w"> </span><span class="kt">NFData</span><span class="p">,</span><span class="w"> </span><span class="kt">Cacheable</span><span class="w"> </span><span class="p">)</span></code></pre><p>This newtype is useless noise. Functionally, it is completely interchangeable with its underlying <code>Name</code> type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an <code>ArgumentName</code>, since the enclosing field name already makes its role clear.</p><p>Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:</p><ul><li><p>Primarily, types distinguish <em>functional</em> differences between values. A value of type <code>NonEmpty a</code> is <em>functionally</em> distinct from a value of type <code>[a]</code>, since it is fundamentally structurally different and permits additional operations. In this sense, types are <em>structural</em>; they describe what values <em>are</em> in the internal world of the programming language.</p></li><li><p>Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate <code>Distance</code> and <code>Duration</code> types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.</p></li></ul><p>Note that both these uses are <em>pragmatic</em>; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system <em>is</em> a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like <code>ArgumentName</code>.</p><p>If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use <code>Name</code>, but in situations where the different label adds genuine clarity, one can always use a type alias:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span></code></pre><p>Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.</p><h2><a name="final-thoughts-and-related-reading"></a>Final thoughts and related reading</h2><p>I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.</p><p>Newtypes are one particular mechanism of defining <em>wrapper types</em>, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.</p><p>The catalyst that got me to finally sit down and write this was the recently-published <a href="https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/">Tagged is not a Newtype</a>. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, <code>Tagged</code> <em>is</em> a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.</p><p>Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.</p><p>And a name is not type safety.</p><ol class="footnotes"><li id="footnote-1"><p>Admittedly rather unlikely given its name, but bear with me through the contrived example. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Types as axioms, or: playing god with static types2020-08-13T00:00:00Z2020-08-13T00:00:00ZAlexis King<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more open2020-01-19T00:00:00Z2020-01-19T00:00:00ZAlexis King<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Parse, don’t validate2019-11-05T00:00:00Z2019-11-05T00:00:00ZAlexis King<article><p>Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.</p><p>However, about a month ago, <a href="https://twitter.com/lexi_lambda/status/1182242561655746560">I was reflecting on Twitter</a> about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:</p><div style="text-align: center; font-size: larger"><strong>Parse, don’t validate.</strong></div><h2><a name="the-essence-of-type-driven-design"></a>The essence of type-driven design</h2><p>Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.</p><h3><a name="the-realm-of-possibility"></a>The realm of possibility</h3><p>One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:</p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Void</span></code></pre><p>Is it possible to implement <code>foo</code>? Trivially, the answer is <em>no</em>, as <code>Void</code> is a type that contains no values, so it’s impossible for <em>any</em> function to produce a value of type <code>Void</code>.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘head’: Patterns not matched: [] +</code></pre><p>This message is helpfully pointing out that our function is <em>partial</em>, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is <code>[]</code>, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.</p><h3><a name="turning-partial-functions-total"></a>Turning partial functions total</h3><p>To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the <code>head</code> function, and we’ll start with the simplest one.</p><h4><a name="managing-expectations"></a>Managing expectations</h4><p>As established, <code>head</code> is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the <code>Maybe</code> type:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span></code></pre><p>This buys us the freedom we need to implement <code>head</code>—it allows us to return <code>Nothing</code> when we discover we can’t produce a value of type <code>a</code> after all:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>Problem solved, right? For the moment, yes… but this solution has a hidden cost.</p><p>Returning <code>Maybe</code> is undoubtably convenient when we’re <em>implementing</em> <code>head</code>. However, it becomes significantly less convenient when we want to actually use it! Since <code>head</code> always has the potential to return <code>Nothing</code>, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">configDirsList</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">configDirsList</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">cacheDir</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="n">cacheDir</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"should never happen; already checked configDirs is non-empty"</span></code></pre><p>When <code>getConfigurationDirectories</code> retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use <code>head</code> in <code>main</code> to get the first element of the list, the <code>Maybe FilePath</code> result still requires us to handle a <code>Nothing</code> case that we know will never happen! This is terribly bad for several reasons:</p><ol><li><p>First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?</p></li><li><p>Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.</p></li><li><p>Finally, and worst of all, this code is a bug waiting to happen! What if <code>getConfigurationDirectories</code> were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update <code>main</code>, and suddenly the “impossible” error becomes not only possible, but probable.</p></li></ol><p>The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically <em>prove</em> the <code>Nothing</code> case impossible, then a modification to <code>getConfigurationDirectories</code> that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.</p><h4><a name="paying-it-forward"></a>Paying it forward</h4><p>Clearly, our modified version of <code>head</code> leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, <code>head</code> should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?</p><p>Let’s look at the original (partial) type signature for <code>head</code> again:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, <code>[a]</code>). Instead of weakening the return type, we can <em>strengthen</em> the argument type, eliminating the possibility of <code>head</code> ever being called on an empty list in the first place.</p><p>To do this, we need a type that represents non-empty lists. Fortunately, the existing <code>NonEmpty</code> type from <code>Data.List.NonEmpty</code> is exactly that. It has the following definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>Note that <code>NonEmpty a</code> is really just a tuple of an <code>a</code> and an ordinary, possibly-empty <code>[a]</code>. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the <code>[a]</code> component is <code>[]</code>, the <code>a</code> component must always be present. This makes <code>head</code> completely trivial to implement:<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Unlike before, GHC accepts this definition without complaint—this definition is <em>total</em>, not partial. We can update our program to use the new implementation:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">FilePath</span><span class="p">)</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">nonEmpty</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="p">)</span></code></pre><p>Note that the redundant check in <code>main</code> is now completely gone! Instead, we perform the check exactly once, in <code>getConfigurationDirectories</code>. It constructs a <code>NonEmpty a</code> from a <code>[a]</code> using the <code>nonEmpty</code> function from <code>Data.List.NonEmpty</code>, which has the following type:</p><pre><code class="pygments"><span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>The <code>Maybe</code> is still there, but this time, we handle the <code>Nothing</code> case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a <code>NonEmpty FilePath</code> value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type <code>NonEmpty a</code> as being like a value of type <code>[a]</code>, plus a <em>proof</em> that the list is non-empty.</p><p>By strengthening the type of the argument to <code>head</code> instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:</p><ul><li><p>The code has no redundant checks, so there can’t be any performance overhead.</p></li><li><p>Furthermore, if <code>getConfigurationDirectories</code> changes to stop checking that the list is non-empty, its return type must change, too. Consequently, <code>main</code> will fail to typecheck, alerting us to the problem before we even run the program!</p></li></ul><p>What’s more, it’s trivial to recover the old behavior of <code>head</code> from the new one by composing <code>head</code> with <code>nonEmpty</code>:</p><pre><code class="pygments"><span class="nf">head&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fmap</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">nonEmpty</span></code></pre><p>Note that the inverse is <em>not</em> true: there is no way to obtain the new version of <code>head</code> from the old one. All in all, the second approach is superior on all axes.</p><h3><a name="the-power-of-parsing"></a>The power of parsing</h3><p>You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:</p><pre><code class="pygments"><span class="nf">validateNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span> + +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="n">xs</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span></code></pre><p>These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: <code>validateNonEmpty</code> always returns <code>()</code>, the type that contains no information, but <code>parseNonEmpty</code> returns <code>NonEmpty a</code>, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but <code>parseNonEmpty</code> gives the caller access to the information it learned, while <code>validateNonEmpty</code> just throws it away.</p><p>These two functions elegantly illustrate two different perspectives on the role of a static type system: <code>validateNonEmpty</code> obeys the typechecker well enough, but only <code>parseNonEmpty</code> takes full advantage of it. If you see why <code>parseNonEmpty</code> is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of <code>parseNonEmpty</code>’s name. Is it really <em>parsing</em> anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe <code>parseNonEmpty</code> is a bona-fide parser (albeit a particularly simple one).</p><p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and <code>parseNonEmpty</code> is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.</p><p>Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:</p><ul><li><p>The <a href="https://hackage.haskell.org/package/aeson">aeson</a> library provides a <code>Parser</code> type that can be used to parse JSON data into domain types.</p></li><li><p>Likewise, <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> provides a set of parser combinators for parsing command-line arguments.</p></li><li><p>Database libraries like <a href="https://hackage.haskell.org/package/persistent">persistent</a> and <a href="https://hackage.haskell.org/package/postgresql-simple">postgresql-simple</a> have a mechanism for parsing values held in an external data store.</p></li><li><p>The <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.</p></li></ul><p>The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.</p><p>One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the <code>NonEmpty</code> example above: if the parsing and processing logic go out of sync, the program will fail to even compile.</p><h3><a name="the-danger-of-validation"></a>The danger of validation</h3><p>Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?</p><p>Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the <a href="http://langsec.org">language-theoretic security</a> field calls <em>shotgun parsing</em>. In the 2016 paper, <a href="http://langsec.org/papers/langsec-cwes-secdev2016.pdf">The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them</a>, its authors provide the following definition:</p><blockquote><p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.</p></blockquote><p>They go on to explain the problems inherent to such validation techniques:</p><blockquote><p>Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.</p></blockquote><p>In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.</p><p>It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.</p><p>Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p><h2><a name="parsing-not-validating-in-practice"></a>Parsing, not validating, in practice</h2><p>So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”</p><p>My advice: focus on the datatypes.</p><p>Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a <code>Map</code>. Adjust your function’s type signature to accept a <code>Map</code> instead of a list of tuples, and implement it as you normally would.</p><p>Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to <code>Map</code>, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of <code>checkNoDuplicateKeys</code>:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span></code></pre><p>Now the check <em>cannot</em> be omitted, since its result is actually necessary for the program to proceed!</p><p>This hypothetical scenario highlights two simple ideas:</p><ol><li><p><strong>Use a data structure that makes illegal states unrepresentable.</strong> Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.</p></li><li><p><strong>Push the burden of proof upward as far as possible, but no further.</strong> Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.</p></li></ol><p>In other words, write functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!</p><p>Here are a handful of additional points of advice, arranged in no particular order:</p><ul><li><p><strong>Let your datatypes inform your code, don’t let your code control your datatypes.</strong> Avoid the temptation to just stick a <code>Bool</code> in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.</p></li><li><p><strong>Treat functions that return <code>m ()</code> with deep suspicion.</strong> Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.</p></li><li><p><strong>Don’t be afraid to parse data in multiple passes.</strong> Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p></li><li><p><strong>Avoid denormalized representations of data, <em>especially</em> if it’s mutable.</strong> Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.</p><ul><li><p><strong>Keep denormalized representations of data behind abstraction boundaries.</strong> If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.</p></li></ul></li><li><p><strong>Use abstract datatypes to make validators “look like” parsers.</strong> Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract <code>newtype</code> with a smart constructor to “fake” a parser from a validator.</p></li></ul><p>As always, use your best judgement. It probably isn’t worth breaking out <a href="https://hackage.haskell.org/package/singletons">singletons</a> and refactoring your entire application just to get rid of a single <code>error "impossible"</code> call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.</p><h2><a name="recap-reflection-and-related-reading"></a>Recap, reflection, and related reading</h2><p>That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.</p><p>None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about <em>process</em>. My hope is that this is a small step in that direction.</p><p>Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post <a href="https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html">Type Safety Back and Forth</a>. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper <a href="https://kataskeue.com/gdp.pdf">Ghosts of Departed Proofs</a>, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.</p><p>As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit <em>any</em> value. These aren’t “real” values (unlike <code>null</code> in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that <a href="https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf">Fast and Loose Reasoning is Morally Correct</a>. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In fact, <code>Data.List.NonEmpty</code> already provides a <code>head</code> function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Empathy and subjective experience in programming languages2019-10-19T00:00:00Z2019-10-19T00:00:00ZAlexis King<article><p>A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.</p><p>Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?</p><p>I think about that question a lot.</p><h2><a name="2015-called-and-they-want-their-dress-back"></a>2015 called, and they want their dress back</h2><p>Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.</p><p>However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into <em>Star Wars</em>. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.</p><p>Take 2015’s phenomenon of “<a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did <em>this</em>—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.</p><h3><a name="when-something-objective-isn-t"></a>When something objective isn’t</h3><p>Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.</p><p>The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.</p><p>In the case of the dress, whether you <a href="https://en.wikipedia.org/wiki/Yanny_or_Laurel">heard “yanny” or “laurel,”</a> or whether you believe the <em>Sonic</em> games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?</p><p>I think the answer is absolutely, unequivocally <em>yes</em>.</p><h2><a name="subjectivity-in-programming-and-in-programming-languages-specifically"></a>Subjectivity in programming, and in programming languages specifically</h2><p>Quick question: which is better, functional or imperative programming?</p><p>My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?</p><p>Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?</p><p>I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> but I did happen to find a link to <a href="https://news.ycombinator.com/item?id=21282647">a recent discussion</a> on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?</p><p>In a <a href="https://news.ycombinator.com/item?id=21284383">branch of the discussion</a>, one user writes:</p><blockquote><blockquote><p>Haskell is great for business and great in production</p></blockquote><p>I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.</p><p>I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.</p></blockquote><p>The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:</p><blockquote><p>I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.</p><p>[…]</p><p>From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.</p></blockquote><p>Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:</p><blockquote><p>Haskell gives one plenty of rope to hang himself on complexity.</p><p>So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.</p></blockquote><p>Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:</p><blockquote><p>Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.</p><p>[…]</p><p>The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.</p><p>Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.</p></blockquote><p>That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?</p><h3><a name="the-unsatisfying-subjective-reality-of-programming-languages"></a>The unsatisfying subjective reality of programming languages</h3><p>You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?</p><p><em><strong>No!</strong></em></p><p>These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, <em>why?</em> Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.</p><p>While <a href="https://news.ycombinator.com/item?id=21284317">one commenter</a> in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” <a href="https://news.ycombinator.com/item?id=21284540">another</a> says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.</p><p>Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t <em>get it.</em> There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.</p><h3><a name="empathy-and-how-bad-results-come-from-good-intentions"></a>Empathy, and how bad results come from good intentions</h3><p>I’ll admit that these kinds of discussions aren’t <em>always</em> fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.</p><p>Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.</p><p>Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:</p><blockquote><p>Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.</p></blockquote><p>I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?</p><p>If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.</p><p>Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.</p><p>To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:</p><blockquote><p>It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.</p><p>Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.</p></blockquote><p>When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective <strong>without</strong> invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about <em>your own</em> experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.</p><p>It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.</p><p>Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be <em>happier.</em></p><ol class="footnotes"><li id="footnote-1"><p>This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is <em>blah</em>,” but you deserve better. So you, uh, get a <em>meta</em> snarky footnote instead. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Which, to be entirely fair, may well be as subjective as anything else in this blog post. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Demystifying MonadBaseControl2019-09-07T00:00:00Z2019-09-07T00:00:00ZAlexis King<article><p><a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl"><code>MonadBaseControl</code> from the <code>monad-control</code> package</a> is a confusing typeclass, and its methods have complicated types. For many people, it’s nothing more than scary, impossible-to-understand magic that is, for some reason, needed when lifting certain kinds of operations. Few resources exist that adequately explain how, why, and when it works, which sadly seems to have resulted in some <a href="https://en.wikipedia.org/wiki/Fear,_uncertainty,_and_doubt">FUD</a> about its use.</p><p>There’s no doubt that the machinery of <code>MonadBaseControl</code> is complex, and the role it plays in practice is often subtle. However, its essence is actually much simpler than it appears, and I promise it can be understood by mere mortals. In this blog post, I hope to provide a complete survey of <code>MonadBaseControl</code>—how it works, how it’s designed, and how it can go wrong—in a way that is accessible to anyone with a firm grasp of monads and monad transformers. To start, we’ll motivate <code>MonadBaseControl</code> by reinventing it ourselves.</p><h2><a name="the-higher-order-action-problem"></a>The higher-order action problem</h2><p>Say we have a function with the following type:<sup><a href="#footnote-0" id="footnote-ref-0-1">1</a></sup></p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>If we have an action built from a transformer stack like</p><pre><code class="pygments"><span class="nf">bar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span></code></pre><p>then we might wish to apply <code>foo</code> to <code>bar</code>, but that is ill-typed, since <code>IO</code> is not the same as <code>StateT X IO</code>. In cases like these, we often use <code>lift</code>, but it’s not good enough here: <code>lift</code> <em>adds</em> a new monad transformer to an action, but here we need to <em>remove</em> a transformer. So we need a function with a type like this:</p><pre><code class="pygments"><span class="nf">unliftState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span></code></pre><p>However, if you think about that type just a little bit, it’s clear something’s wrong: it throws away information, namely the state. You may remember that a <code>StateT X IO Y</code> action is equivalent to a function of type <code>X -&gt; IO (Y, X)</code>, so our hypothetical <code>unliftState</code> function has two problems:</p><ol><li><p>We have no <code>X</code> to use as the initial state.</p></li><li><p>We’ll lose any modifications <code>bar</code> made to the state, since the result type is just <code>Y</code>, not <code>(Y, X)</code>.</p></li></ol><p>Clearly, we’ll need something more sophisticated, but what?</p><h2><a name="a-na-ve-solution"></a>A naïve solution</h2><p>Given that <code>foo</code> doesn’t know anything about the state, we can’t easily thread it through <code>foo</code> itself. However, by using <code>runStateT</code> explicitly, we could do some of the state management ourselves:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">v</span></code></pre><p>Do you see what’s going on there? It’s not actually very complicated: we get the current state, then pass it as the initial state to <code>runStateT</code>. This produces an action <code>IO (a, s)</code> that has <em>closed over</em> the current state. We can pass that action to <code>foo</code> without issue, since <code>foo</code> is polymorphic in the action’s return type. Finally, all we have to do is <code>put</code> the modified state back into the enclosing <code>StateT</code> computation, and we can get on with our business.</p><p>That strategy works okay when we only have one monad transformer, but it gets hairy quickly as soon as we have two or more. For example, if we had <code>baz :: ExceptT X (StateT Y IO) Z</code>, then we <em>could</em> do the same trick by getting the underlying</p><pre><code class="pygments"><span class="kt">Y</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">Z</span><span class="p">,</span><span class="w"> </span><span class="kt">Y</span><span class="p">)</span></code></pre><p>function, closing over the state, restoring it, and doing the appropriate case analysis to re-raise any <code>ExceptT</code> errors, but that’s a lot of work to do for every single function! What we’d like to do instead is somehow abstract over the pattern we used to write <code>foo'</code> in a way that scales to arbitrary monad transformers.</p><h2><a name="the-essence-of-monadbasecontrol"></a>The essence of <code>MonadBaseControl</code></h2><p>To build a more general solution for “unlifting” arbitrary monad transformers, we need to start thinking about monad transformer state. The technique we used to implement <code>foo'</code> operated on the following process:</p><ol><li><p>Capture the action’s input state and close over it.</p></li><li><p>Package up the action’s output state with its result and run it.</p></li><li><p>Restore the action’s output state into the enclosing transformer.</p></li><li><p>Return the action’s result.</p></li></ol><p>For <code>StateT s</code>, it turns out that the input state and output state are both <code>s</code>, but other monad transformers have state, too. Consider the input and output state for the following common monad transformers:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>StateT s m a</code></td> + <td><code>s -&gt; m (a, s)</code></td> + <td><code>s</code></td> + <td><code>s</code></td> + </tr> + <tr> + <td><code>ReaderT r m a</code></td> + <td><code>r -&gt; m a</code></td> + <td><code>r</code></td> + <td><code>()</code></td> + </tr> + <tr> + <td><code>WriterT w m a</code></td> + <td><code>m (a, w)</code></td> + <td><code>()</code></td> + <td><code>w</code></td> + </tr> + </table> +</div><p>Notice how the input state is whatever is to the left of the <code>-&gt;</code>, while the output state is whatever extra information gets produced alongside the result. Using the same reasoning, we can also deduce the input and output state for compositions of multiple monad transformers, such as the following:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>ReaderT r (WriterT w m) a</code></td> + <td><code>r -&gt; m (a, w)</code></td> + <td><code>r</code></td> + <td><code>w</code></td> + </tr> + <tr> + <td><code>StateT s (ReaderT r m) a</code></td> + <td><code>r -&gt; s -&gt; m (a, s)</code></td> + <td><code>(r, s)</code></td> + <td><code>s</code></td> + </tr> + <tr> + <td><code>WriterT w (StateT s m) a</code></td> + <td><code>s -&gt; m ((a, w), s)</code></td> + <td><code>s</code></td> + <td><code>(w, s)</code></td> + </tr> + </table> +</div><p>Notice that when monad transformers are composed, their states are composed, too. This is useful to keep in mind, since our goal is to capture the four steps above in a typeclass, polymorphic in the state of the monad transformers we need to lift through. At minimum, we need two new operations: one to capture the input state and close over it (step 1) and one to restore the output state (step 3). One class we might come up with could look like this:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>If we can write instances of that typeclass for various transformers, we can use the class’s operations to implement <code>foo'</code> in a generic way that works with any combination of them:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">v</span></code></pre><p>So how do we implement those instances? Let’s start with <code>IO</code>, since that’s the base case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;&amp;&gt;</span><span class="w"> </span><span class="p">(,</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Not very exciting. The <code>StateT s</code> instance, on the other hand, is significantly more interesting:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(,)</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&lt;*&gt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span></code></pre><p><strong>This instance alone includes most of the key ideas behind <code>MonadBaseControl</code>.</strong> There’s a lot going on, so let’s break it down, step by step:</p><ol><li><p>Start by examining the definitions of <code>InputState</code> and <code>OutputState</code>. Are they what you expected? You’d be forgiven for expecting the following:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">s</span> +<span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">s</span></code></pre><p>After all, that’s what we wrote in the table, isn’t it?</p><p>However, if you give it a try, you’ll find it doesn’t work. <code>InputState</code> and <code>OutputState</code> must capture the state of the <em>entire</em> monad, not just a single transformer layer, so we have to combine the <code>StateT s</code> state with the state of the underlying monad. In the simplest case we get</p><pre><code class="pygments"><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>which is boring, but in a more complex case, we need to get something like this:</p><pre><code class="pygments"><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="kt">IO</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="nb">()</span><span class="p">))</span></code></pre><p>Therefore, <code>InputState (StateT s m)</code> combines <code>s</code> with <code>InputState m</code> in a tuple, and <code>OutputState</code> does the same.</p></li><li><p>Moving on, take a look at <code>captureInputState</code> and <code>closeOverInputState</code>. Just as <code>InputState</code> and <code>OutputState</code> capture the state of the entire monad, these functions need to be inductive in the same way.</p><p><code>captureInputState</code> acquires the current state using <code>get</code>, and it combines it with the remaining monadic state using <code>lift captureInputState</code>. <code>closeOverInputState</code> uses the captured state to peel off the outermost <code>StateT</code> layer, then calls <code>closeOverInputState</code> recursively to peel off the rest of them.</p></li><li><p>Finally, <code>restoreOutputState</code> restores the state of the underlying monad stack, then restores the <code>StateT</code> state, ensuring everything ends up back the way it’s supposed to be.</p></li></ol><p>Take the time to digest all that—work through it yourself if you need to—as it’s a dense piece of code. Once you feel comfortable with it, take a look at the instances for <code>ReaderT</code> and <code>WriterT</code> as well:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(,)</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="o">&lt;*&gt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span></code></pre><p>Make sure you understand these instances, too. It should be easier this time, since they share most of their structure with the <code>StateT</code> instance, but note the asymmetry that arises from the differing input and output states. (It may even help to try and write these instances yourself, focusing on the types whenever you get stuck.)</p><p>If you feel alright with them, then congratulations: you’re already well on your way to grokking <code>MonadBaseControl</code>!</p><h3><a name="hiding-the-input-state"></a>Hiding the input state</h3><p>So far, our implementation of <code>MonadBaseControl</code> works, but it’s actually slightly more complicated than it needs to be. As it happens, all valid uses of <code>MonadBaseControl</code> will always end up performing the following pattern:</p><pre><code class="pygments"><span class="nf">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="kr">let</span><span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span></code></pre><p>That is, we close over the input state as soon as we capture it. We can therefore combine <code>captureInputState</code> and <code>closeOverInputState</code> into a single function:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">))</span></code></pre><p>What’s more, we no longer need the <code>InputState</code> associated type at all! This is an improvement, since it simplifies the API and removes the possibility for any misuse of the input state, since it’s never directly exposed. On the other hand, it has a more complicated type: it produces a monadic action <em>that returns another monadic action</em>. This can be a little more difficult to grok, which is why I presented the original version first, but it may help to consider how the above type arises naturally from the following definition:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">captureInputState</span></code></pre><p>Let’s update the <code>MonadBaseControl</code> class to incorporate this simplification:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>We can then update all the instances to use the simpler API by simply fusing the definitions of <code>captureInputState</code> and <code>closeOverInputState</code> together:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">&lt;&amp;&gt;</span><span class="w"> </span><span class="p">(,</span><span class="w"> </span><span class="nb">()</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span></code></pre><p>This is already very close to a full <code>MonadBaseControl</code> implementation. The <code>captureAndCloseOverInputState</code> implementations are getting a little out of hand, but bear with me—they’ll get simpler before this blog post is over.</p><h3><a name="coping-with-partiality"></a>Coping with partiality</h3><p>Our <code>MonadBaseControl</code> class now works with <code>StateT</code>, <code>ReaderT</code>, and <code>WriterT</code>, but one transformer we haven’t considered is <code>ExceptT</code>. Let’s try to extend our table from before with a row for <code>ExceptT</code>:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>ExceptT e m a</code></td> + <td><code>m (Either e a)</code></td> + <td><code>()</code></td> + <td><code>???</code></td> + </tr> + </table> +</div><p>Hmm… what <em>is</em> the output state for <code>ExceptT</code>?</p><p>The answer can’t be <code>e</code>, since we might not end up with an <code>e</code>—the computation might not fail. <code>Maybe e</code> would be closer… could that work?</p><p>Well, let’s try it. Let’s write a <code>MonadBaseControl</code> instance for <code>ExceptT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Sadly, the above implementation doesn’t typecheck; it is rejected with the following type error:</p><pre><code>• Couldn't match type ‘Either e a’ with ‘(a, Maybe e)’ + Expected type: m (b ((a, Maybe e), OutputState m)) + Actual type: m (b (Either e a, OutputState m)) +• In the second argument of ‘($)’, namely + ‘captureAndCloseOverInputState (runExceptT m)’ + In a stmt of a 'do' block: + m' &lt;- lift $ captureAndCloseOverInputState (runExceptT m) + In the expression: + do m' &lt;- lift $ captureAndCloseOverInputState (runExceptT m) + return do ((v, s'), ss') &lt;- m' + pure (v, (s', ss')) +</code></pre><p>We promised a <code>(a, Maybe e)</code>, but we have an <code>Either e a</code>, and there’s certainly no way to get the former from the latter. Are we stuck? (If you’d like, take a moment to think about how you’d solve this type error before moving on, as it may be helpful for understanding the following solution.)</p><p>The fundamental problem here is <em>partiality</em>. The type of the <code>captureAndCloseOverInputState</code> method always produces an action in the base monad that includes an <code>a</code> <em>in addition</em> to some other output state. But <code>ExceptT</code> is different: when it an error is raised, it doesn’t produce an <code>a</code> at all—it only produces an <code>e</code>. Therefore, as written, it’s impossible to give <code>ExceptT</code> a <code>MonadBaseControl</code> instance.</p><p>Of course, we’d very much <em>like</em> to give <code>ExceptT</code> a <code>MonadBaseControl</code> instance, so that isn’t very satisfying. Somehow, we need to change <code>captureAndCloseOverInputState</code> so that it doesn’t always need to produce an <code>a</code>. There are a few ways we could accomplish that, but an elegant way to do it is this:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>We’ve replaced the old <code>OutputState</code> associated type with a new <code>WithOutputState</code> type, and the key difference between them is that <code>WithOutputState</code> describes the type of a <em>combination</em> of the result (of type <code>a</code>) and the output state, rather than describing the type of the output state alone. For total monad transformers like <code>StateT</code>, <code>ReaderT</code>, and <code>WriterT</code>, <code>WithOutputState m a</code> will just be a tuple of the result value and the output state, the same as before. For example, here’s an updated <code>MonadBaseControl</code> instance for <code>StateT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">a</span></code></pre><p>Before we consider how this helps us with <code>ExceptT</code>, let’s pause for a moment and examine the revised <code>StateT</code> instance in detail, as there are some new things going on here:</p><ul><li><p>Take a close look at the definition of <code>WithOutputState (StateT s m) a</code>. Note that we’ve defined it to be <code>WithOutputState m (a, s)</code>, <em>not</em> <code>(WithOutputState m a, s)</code>. Consider, for a moment, the difference between these types. Can you see why we used the former, not the latter?</p><p>If it’s unclear to you, that’s okay—let’s illustrate the difference with an example. Consider two similar monad transformer stacks:</p><pre><code class="pygments"><span class="nf">m1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="n">a</span> +<span class="nf">m2</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="n">a</span></code></pre><p>Both these stacks contain <code>StateT</code> and <code>ExceptT</code>, but they are layered in a different order. What’s the difference? Well, consider what <code>m1</code> and <code>m2</code> return once fully unwrapped:</p><pre><code class="pygments"><span class="nf">runExceptT</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m1</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))</span> +<span class="nf">runStateT</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m2</span><span class="p">)</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span></code></pre><p>These results are meaningfully different: in <code>m1</code>, the state is discarded if an error is raised, but in <code>m2</code>, the final state is always returned, even if the computation is aborted. What does this mean for <code>WithOutputState</code>?</p><p>Here’s the important detail: <strong>the state is discarded when <code>ExceptT</code> is “inside” <code>StateT</code>, not the other way around.</strong> This can be counterintuitive, since the <code>s</code> ends up <em>inside</em> the <code>Either</code> when the <code>StateT</code> constructor is on the <em>outside</em> and vice versa. This is really just a property of how monad transformers compose, not anything specific to <code>MonadBaseControl</code>, so an explanation of why this happens is outside the scope of this blog post, but the relevant insight is that the <code>m</code> in <code>StateT s m a</code> controls the eventual action’s output state.</p><p>If we had defined <code>WithOutputState (StateT s m) a</code> to be <code>(WithOutputState m a, s)</code>, we’d be in a pickle, since <code>m</code> would be unable to influence the presence of <code>s</code> in the output state. Therefore, we have no choice but to use <code>WithOutputState m (a, s)</code>. (If you are still confused by this, try it yourself; you’ll find that there’s no way to make the other definition typecheck.)</p></li><li><p>Now that we’ve developed an intuitive understanding of why <code>WithOutputState</code> must be defined the way it is, let’s look at things from another perspective. Consider the type of <code>runStateT</code> once more:</p><pre><code class="pygments"><span class="nf">runStateT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span></code></pre><p>Note that the result type is <code>m (a, s)</code>, with the <code>m</code> on the outside. As it happens, this correspondence simplifies the definition of <code>captureAndCloseOverInputState</code>, since we no longer have to do any fiddling with its result—it’s already in the proper shape, so we can just return it directly.</p></li><li><p>Finally, this instance illustrates an interesting change to <code>restoreOutputState</code>. Since the <code>a</code> is now packed inside the <code>WithOutputState m a</code> value, the caller of <code>captureAndCloseOverInputState</code> needs some way to get the <code>a</code> back out! Conveniently, <code>restoreOutputState</code> can play that role, both restoring the output state and unpacking the result.</p><p>Even ignoring partial transformers like <code>ExceptT</code>, this is an improvement over the old API, as it conveniently prevents the programmer from forgetting to call <code>restoreOutputState</code>. However, as we’ll see shortly, it is much more than a convenience: once <code>ExceptT</code> comes into play, it is essential!</p></li></ul><p>With those details addressed, let’s return to <code>ExceptT</code>. Using the new interface, writing an instance for <code>ExceptT</code> is not only possible, it’s actually rather easy:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">either</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span></code></pre><p>This instance illustrates why it’s so crucial that <code>restoreOutputState</code> have the aforementioned dual role: it must handle the case where no <code>a</code> exists at all! In the case of <code>ExceptT</code>, it restores the state in the enclosing monad by re-raising an error.</p><p>Now all that’s left to do is update the other instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">a</span></code></pre><p>Finally, we can update our lifted variant of <code>foo</code> to use the new interface so it will work with transformer stacks that include <code>ExceptT</code>:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span></code></pre><p>At this point, it’s worth considering something: although getting the <code>MonadBaseControl</code> class and instances right was a lot of work, the resulting <code>foo'</code> implementation is actually incredibly simple. That’s a good sign, since we only have to write the <code>MonadBaseControl</code> instances once (in a library), but we have to write functions like <code>foo'</code> quite often.</p><h2><a name="scaling-to-the-real-monadbasecontrol"></a>Scaling to the real <code>MonadBaseControl</code></h2><p>The <code>MonadBaseControl</code> class we implemented in the previous section is complete. It is a working, useful class that is equivalent in power to <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl">the “real” <code>MonadBaseControl</code> class in the <code>monad-control</code> library</a>. However, if you compare the two, you’ll notice that the version in <code>monad-control</code> looks a little bit different. What gives?</p><p>Let’s compare the two classes side by side:</p><pre><code class="pygments"><span class="c1">-- ours</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> + +<span class="c1">-- theirs</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Let’s start with the similarities, since those are easy:</p><ul><li><p>Our <code>WithOutputState</code> associated type is precisely equivalent to their <code>StM</code> associated type, they just use a (considerably) shorter name.</p></li><li><p>Likewise, our <code>restoreOutputState</code> method is precisely equivalent to their <code>restoreM</code> method, simply under a different name.</p></li></ul><p>That leaves <code>captureAndCloseOverInputState</code> and <code>liftBaseWith</code>. Those two methods both do similar things, but they aren’t identical, and that’s where all the differences lie. To understand <code>liftBaseWith</code>, let’s start by inlining the definition of the <code>RunInBase</code> type alias so we can see the fully-expanded type:</p><pre><code class="pygments"><span class="nf">liftBaseWith</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">((</span><span class="n">forall</span><span class="w"> </span><span class="n">c</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">c</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>That type is complicated! However, if we break it down, hopefully you’ll find it’s not as scary as it first appears. Let’s reimplement the <code>foo'</code> example from before using <code>liftBaseWith</code> to show how this version of <code>MonadBaseControl</code> works:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>This is, in some ways, superficially similar to the version we wrote using our version of <code>MonadBaseControl</code>. Just like in our version, we capture the input state, apply <code>foo</code> in the <code>IO</code> monad, then restore the state. But what exactly is doing the state capturing, and what is <code>runInBase</code>?</p><p>Let’s start by adding a type annotation to <code>runInBase</code> to help make it a little clearer what’s going on:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">b</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>That type should look sort of recognizable. If we replace <code>StM</code> with <code>WithOutputState</code>, then we get a type that looks very similar to that of our original <code>closeOverInputState</code> function, except it doesn’t need to take the input state as an argument. How does that work?</p><p>Here’s the trick: <code>liftBaseWith</code> starts by capturing the input state, just as before. However, it then builds a function, <code>runInBase</code>, which is like <code>closeOverInputState</code> partially-applied to the input state it captured. It hands that function to us, and we’re free to apply it to <code>m</code>, which produces the <code>IO (StM m a)</code> action we need, and we can now pass that action to <code>foo</code>. The result is returned in the outer monad, and we restore the state using <code>restoreM</code>.</p><h3><a name="sharing-the-input-state"></a>Sharing the input state</h3><p>At first, this might seem needlessly complicated. When we first started, we separated capturing the input state and closing over it into two separate operations (<code>captureInputState</code> and <code>closeOverInputState</code>), but we eventually combined them so that we could keep the input state hidden. Why does <code>monad-control</code> split them back into two operations again?</p><p>As it turns out, when lifting <code>foo</code>, there’s no advantage to the more complicated API of <code>monad-control</code>. In fact, we could implement our <code>captureAndCloseOverInputState</code> operation in terms of <code>liftBaseWith</code>, and we could use that to implement <code>foo'</code> the same way we did before:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> + +<span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span></code></pre><p>However, that approach has a downside once we need to lift more complicated functions. <code>foo</code> is exceptionally simple, as it only accepts a single input argument, but what if we wanted to lift a more complicated function that took <em>two</em> monadic arguments, such as this one:</p><pre><code class="pygments"><span class="nf">bar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>We could implement that by calling <code>captureAndCloseOverInputState</code> twice, like this:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">ma&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">ma</span> +<span class="w"> </span><span class="n">mb&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">mb</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">bar</span><span class="w"> </span><span class="n">ma&#39;</span><span class="w"> </span><span class="n">mb&#39;</span><span class="p">)</span></code></pre><p>However, that would capture the monadic state twice, which is rather inefficient. By using <code>liftBaseWith</code>, the state capturing is done just once, and it’s shared between all calls to <code>runInBase</code>:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>By providing a “running” function (<code>runInBase</code>) instead of direct access to the input state, <code>liftBaseWith</code> allows sharing the captured input state between multiple actions without exposing it directly.</p><h3><a name="sidebar-continuation-passing-and-impredicativity"></a>Sidebar: continuation-passing and impredicativity</h3><p>One last point before we move on: although the above explains why <code>captureAndCloseOverInputState</code> is insufficient, you may be left wondering why <code>liftBaseWith</code> can’t just <em>return</em> <code>runInBase</code>. Why does it need to be given a continuation? After all, it would be nicer if we could just write this:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">askRunInBase</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">))</span></code></pre><p>To understand the problem with a hypothetical <code>askRunInBase</code> function, remember that the type of <code>runInBase</code> is polymorphic:</p><pre><code class="pygments"><span class="nf">runInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This is important, since if you need to lift a function with a type like</p><pre><code class="pygments"><span class="nf">baz</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)</span></code></pre><p>then you’ll want to instantiate that <code>a</code> variable with two different types. We’d need to retain that power in <code>askRunInBase</code>, so it would need to have the following type:</p><pre><code class="pygments"><span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span></code></pre><p>Sadly, that type is illegal in Haskell. Type constructors must be applied to monomorphic types, but in the above type signature, <code>m</code> is applied to a polymorphic type.<sup><a href="#footnote-1" id="footnote-ref-1-1">2</a></sup> The <code>RankNTypes</code> GHC extension introduces a single exception: the <code>(-&gt;)</code> type constructor is special and may be applied to polymorphic types. That’s why <code>liftBaseWith</code> is legal, but <code>askRunInBase</code> is not: since <code>liftBaseWith</code> is passed a higher-order function that receives <code>runInBase</code> as an argument, the polymorphic type appears immediately under an application of <code>(-&gt;)</code>, which is allowed.</p><p>The aforementioned restriction means we’re basically out of luck, but if you <em>really</em> want <code>askRunInBase</code>, there is a workaround. GHC is perfectly alright with a field of a datatype being polymorphic, so we can define a newtype that wraps a suitably-polymorphic function:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span></code></pre><p>We can now alter <code>askRunInBase</code> to return our newtype, and we can implement it in terms of <code>liftBaseWith</code>:<sup><a href="#footnote-2" id="footnote-ref-2-1">3</a></sup></p><pre><code class="pygments"><span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">runInBase</span></code></pre><p>To use <code>askRunInBase</code>, we have to pattern match on the <code>RunInBase</code> constructor, but it isn’t very noisy, since we can do it directly in a <code>do</code> binding. For example, we could implement a lifted version of <code>baz</code> this way:</p><pre><code class="pygments"><span class="nf">baz&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="nf">baz&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">askRunInBase</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">))</span> +<span class="w"> </span><span class="n">bitraverse</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>As of version 1.0.2.3, <code>monad-control</code> does not provide a newtype like <code>RunInBase</code>, so it also doesn’t provide a function like <code>askRunInBase</code>. For now, you’ll have to use <code>liftBaseWith</code>, but it might be a useful future addition to the library.</p><h2><a name="pitfalls"></a>Pitfalls</h2><p>At this point in the blog post, we’ve covered the essentials of <code>MonadBaseControl</code>: how it works, how it’s designed, and how you might go about using it. However, so far, we’ve only considered situations where <code>MonadBaseControl</code> works well, and I’ve intentionally avoided examples where the technique breaks down. In this section, we’re going to take a look at the pitfalls and drawbacks of <code>MonadBaseControl</code>, plus some ways they can be mitigated.</p><h3><a name="no-polymorphism-no-lifting"></a>No polymorphism, no lifting</h3><p>All of the pitfalls of <code>MonadBaseControl</code> stem from the same root problem, and that’s the particular technique it uses to save and restore monadic state. We’ll start by considering one of the simplest ways that technique is thwarted, and that’s monomorphism. Consider the following two functions:</p><pre><code class="pygments"><span class="nf">poly</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span> +<span class="nf">mono</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">X</span></code></pre><p>Even after all we’ve covered, it may surprise you to learn that although <code>poly</code> can be easily lifted to <code>MonadBaseControl IO m =&gt; m a -&gt; m a</code>, it’s <em>impossible</em> to lift <code>mono</code> to <code>MonadBaseControl IO m =&gt; m X -&gt; m X</code>. It’s a little unintuitive, as we often think of polymorphic types as being more complicated (so surely lifting polymorphic functions ought to be harder), but in fact, it’s the flexibility of polymorphism that allows <code>MonadBaseControl</code> to work in the first place.</p><p>To understand the problem, remember that when we lift a function of type <code>forall a. b a -&gt; b a</code> using <code>MonadBaseControl</code>, we actually instantiate <code>a</code> to <code>(StM m c)</code>. That produces a function of type <code>b (StM m c) -&gt; b (StM m c)</code>, which is isomorphic to the <code>m c -&gt; m c</code> type we want. The instantiation step is easily overlooked, but it’s crucial, since otherwise we have no way to thread the state through the otherwise opaque function we’re trying to lift!</p><p>In the case of <code>mono</code>, that’s exactly the problem we’re faced with. <code>mono</code> will not accept an <code>IO (StM m X)</code> as an argument, only precisely an <code>IO X</code>, so we can’t pass along the monadic state. For all its machinery, <code>MonadBaseControl</code> is no help at all if no polymorphism is involved. Trying to generalize <code>mono</code> without modifying its implementation is a lost cause.</p><h3><a name="the-dangers-of-discarded-state"></a>The dangers of discarded state</h3><p>Our inability to lift <code>mono</code> is frustrating, but at least it’s conclusively impossible. In practice, however, many functions lie in an insidious in-between: polymorphic enough to be lifted, but not without compromises. The simplest of these functions have types such as the following:</p><pre><code class="pygments"><span class="nf">sideEffect</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Unlike <code>mono</code>, it’s entirely possible to lift <code>sideEffect</code>:</p><pre><code class="pygments"><span class="nf">sideEffect&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">sideEffect&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">sideEffect</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>This definition typechecks, but you may very well prefer it didn’t, since it has a serious problem: any changes made by <code>m</code> to the monadic state are completely discarded once <code>sideEffect'</code> returns! Since <code>sideEffect'</code> never calls <code>restoreM</code>, there’s no way the state of <code>m</code> can be any different from the original state, but it’s impossible to call <code>restoreM</code> since we don’t actually get an <code>StM m ()</code> result from <code>sideEffect</code>.</p><p>Sometimes this may be acceptable, since some monad transformers don’t actually have any output state anyway, such as <code>ReaderT r</code>. In other cases, however, <code>sideEffect'</code> could be a bug waiting to happen. One way to make <code>sideEffect'</code> safe would be to add a <code>StM m a ~ a</code> constraint to its context, since that guarantees the monad transformers being lifted through are stateless, and nothing is actually being discarded. Of course, that significantly restricts the set of monad transformers that can be lifted through.</p><h4><a name="rewindable-state"></a>Rewindable state</h4><p>One scenario where state discarding can actually be useful is operations with so-called rewindable or transactional state. The most common example of such an operation is <code>catch</code>:</p><pre><code class="pygments"><span class="nf">catch</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Exception</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>When lifted, state changes from the action <em>or</em> from the exception handler will be “committed,” but never both. If an exception is raised during the computation, those state changes are discarded (“rewound”), giving <code>catch</code> a kind of backtracking semantics. This behavior arises naturally from the way a lifted version of <code>catch</code> must be implemented:</p><pre><code class="pygments"><span class="nf">catch&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Exception</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">catch&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">catch</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>If <code>m</code> raises an exception, it will never return an <code>StM m a</code> value, so there’s no way to get ahold of any of the state changes that happened before the exception. Therefore, the only option is to discard that state.</p><p>This behavior is actually quite useful, and it’s definitely not unreasonable. However, useful or not, it’s inconsistent with state changes to mutable values like <code>IORef</code>s or <code>MVar</code>s (they stay modified whether an exception is raised or not), so it can still be a gotcha. Either way, it’s worth being aware of.</p><h4><a name="partially-discarded-state"></a>Partially discarded state</h4><p>The next function we’re going to examine is <code>finally</code>:</p><pre><code class="pygments"><span class="nf">finally</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function has a similar type to <code>catch</code>, and it even has similar semantics. Like <code>catch</code>, <code>finally</code> can be lifted, but unlike <code>catch</code>, its state <em>can’t</em> be given any satisfying treatment. The only way to implement a lifted version is</p><pre><code class="pygments"><span class="nf">finally&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">finally&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">finally</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>which always discards all state changes made by the second argument. This is clear just from looking at <code>finally</code>’s type: since <code>b</code> doesn’t appear anywhere in the return type, there’s simply no way to access that action’s result, and therefore no way to access its modified state.</p><p>However, don’t despair: there actually <em>is</em> a way to produce a lifted version of <code>finally</code> that preserves all state changes. It can’t be done by lifting <code>finally</code> directly, but if we reimplement <code>finally</code> in terms of simpler lifted functions that are more amenable to lifting, we can produce a lifted version of <code>finally</code> that preserves all the state:<sup><a href="#footnote-3" id="footnote-ref-3-1">4</a></sup></p><pre><code class="pygments"><span class="nf">finally&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">finally&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mask&#39;</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">restore</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">try</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="p">(</span><span class="n">restore</span><span class="w"> </span><span class="n">ma</span><span class="p">))</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">throwIO</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SomeException</span><span class="p">))</span> +<span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">&lt;*</span><span class="w"> </span><span class="n">mb</span></code></pre><p>This illustrates an important (and interesting) point about <code>MonadBaseControl</code>: whether or not an operation can be made state-preserving is not a fundamental property of the operation’s type, but rather a property of the types of the exposed primitives. There is sometimes a way to implement a state-preserving variant of operations that might otherwise seem unliftable given the right primitives and a bit of cleverness.</p><h4><a name="forking-state"></a>Forking state</h4><p>As a final example, I want to provide an example where the state may not actually be discarded <em>per se</em>, just inaccessible. Consider the type of <code>forkIO</code>:</p><pre><code class="pygments"><span class="nf">forkIO</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">ThreadId</span></code></pre><p>Although <code>forkIO</code> isn’t actually polymorphic in its argument, we can convert <em>any</em> <code>IO</code> action to one that produces <code>()</code> via <code>void</code>, so it might as well be. Therefore, we can lift <code>forkIO</code> in much the same way we did with <code>sideEffect</code>:</p><pre><code class="pygments"><span class="nf">forkIO&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">ThreadId</span> +<span class="nf">forkIO&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">forkIO</span><span class="w"> </span><span class="p">(</span><span class="n">void</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>As with <code>sideEffect</code>, we can’t recover the output state, but in this case, there’s a fundamental reason that goes deeper than the types: we’ve forked off a concurrent computation! We’ve therefore split the state in two, which might be what we want… but it also might not. <code>forkIO</code> is yet another illustration that it’s important to think about the state-preservation semantics when using <code>MonadBaseControl</code>, or you may end up with a bug!</p><h2><a name="monadbasecontrol-in-context"></a><code>MonadBaseControl</code> in context</h2><p>Congratulations: you’ve made it through most of this blog post. If you’ve followed everything so far, you now understand <code>MonadBaseControl</code>. All the tricky parts are over. However, before wrapping up, I’d like to add a little extra information about how <code>MonadBaseControl</code> relates to various other parts of the Haskell ecosystem. In practice, that information can be as important as understanding <code>MonadBaseControl</code> itself.</p><h3><a name="the-remainder-of-monad-control"></a>The remainder of <code>monad-control</code></h3><p>If you look at <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html">the documentation for <code>monad-control</code></a>, you’ll find that it provides more than just the <code>MonadBaseControl</code> typeclass. I’m not going to cover everything else in detail in this blog post, but I do want to touch upon it briefly.</p><p>First off, you should definitely take a look at the handful of helper functions provided by <code>monad-control</code>, such as <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:control"><code>control</code></a> and <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:liftBaseOp_"><code>liftBaseOp_</code></a>. These functions provide support for lifting common function types without having to use <code>liftBaseWith</code> directly. It’s useful to understand <code>liftBaseWith</code>, since it’s the most general way to use <code>MonadBaseControl</code>, but in practice, it is simpler and more readable to use the more specialized functions wherever possible. Many of the examples in this very blog post could be simplified using them, and I only stuck to <code>liftBaseWith</code> to introduce as few new concepts at a time as possible.</p><p>Second, I’d like to mention the related <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadTransControl"><code>MonadTransControl</code></a> typeclass. You hopefully remember from earlier in the blog post how we defined <code>MonadBaseControl</code> instances inductively so that we could lift all the way down to the base monad. <code>MonadTransControl</code> is like <code>MonadBaseControl</code> if it intentionally did <em>not</em> do that—it allows lifting through a single transformer at a time, rather than through all of them at once.</p><p>Usually, <code>MonadTransControl</code> is not terribly useful to use directly (though I did use it once <a href="/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/#making-mtls-classes-derivable">in a previous blog post of mine</a> to help derive instances of mtl-style classes), but it <em>is</em> useful for implementing <code>MonadBaseControl</code> instances for your own transformers. If you define a <code>MonadTransControl</code> instance for your monad transformer, you can get a <code>MonadBaseControl</code> implementation for free using the provided <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:ComposeSt"><code>ComposeSt</code></a>, <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultLiftBaseWith"><code>defaultLiftBaseWith</code></a>, and <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultRestoreM"><code>defaultRestoreM</code></a> bindings; see the documentation for more details.</p><h3><a name="lifted-base-and-lifted-async"></a><code>lifted-base</code> and <code>lifted-async</code></h3><p>If you’re going to use <code>MonadBaseControl</code>, the <a href="http://hackage.haskell.org/package/lifted-base"><code>lifted-base</code></a> and <a href="http://hackage.haskell.org/package/lifted-async"><code>lifted-async</code></a> packages are good to know about. As their names imply, they provide lifted versions of bindings in the <code>base</code> and <code>async</code> packages, so you can use them directly without needing to lift them yourself. For example, if you needed a lifted version of <code>mask</code> from <code>Control.Exception</code>, you could swap it for the <code>mask</code> export from <code>Control.Exception.Lifted</code>, and everything would mostly just work (though always be sure to check the documentation for any caveats on state discarding).</p><h3><a name="relationship-to-monadunliftio"></a>Relationship to <code>MonadUnliftIO</code></h3><p>Recently, FP Complete has developed the <a href="https://hackage.haskell.org/package/unliftio"><code>unliftio</code></a> package as an alternative to <code>monad-control</code>. It provides the <a href="https://hackage.haskell.org/package/unliftio-core-0.1.2.0/docs/Control-Monad-IO-Unlift.html#t:MonadUnliftIO"><code>MonadUnliftIO</code></a> typeclass, which is similar in spirit to <code>MonadBaseControl</code>, but heavily restricted: it is specialized to <code>IO</code> as the base monad, and it <em>only</em> allows instances for stateless monads, such as <code>ReaderT</code>. This is designed to encourage the so-called <a href="https://www.fpcomplete.com/blog/2017/06/readert-design-pattern"><code>ReaderT</code> design pattern</a>, which avoids ever using stateful monads like <code>ExceptT</code> or <code>StateT</code> over <code>IO</code>, encouraging the use of <code>IO</code> exceptions and mutable variables (e.g. <code>MVar</code>s or <code>TVar</code>s) instead.</p><p>I should be clear: I really like most of what FP Complete has done—to this day, I still use <code>stack</code> as my Haskell build tool of choice—and I think the suggestions given in the aforementioned “<code>ReaderT</code> design pattern” blog post have real weight to them. I have a deep respect for Michael Snoyman’s commitment to opinionated, user-friendly tools and libraries. But truthfully, I can’t stand <code>MonadUnliftIO</code>.</p><p><code>MonadUnliftIO</code> is designed to avoid all the complexity around state discarding that <code>MonadBaseControl</code> introduces, and on its own, that’s a noble goal. Safety first, after all. The problem is that <code>MonadUnliftIO</code> really is extremely limiting, and what’s more, it can actually be trivially encoded in terms of <code>MonadBaseControl</code> as follows:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">MonadUnliftIO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This alias can be used to define safe, lifted functions that never discard state while still allowing functions that <em>can</em> be safely lifted through stateful transformers to do so. Indeed, the <a href="https://hackage.haskell.org/package/lifted-async-0.10.0.4/docs/Control-Concurrent-Async-Lifted-Safe.html"><code>Control.Concurrent.Async.Lifted.Safe</code></a> module from <code>lifted-async</code> does exactly that (albeit with a slightly different formulation than the above alias).</p><p>To be fair, the <code>unliftio</code> README does address this in its <a href="https://github.com/fpco/unliftio/tree/bb2e26e7fbbaebb15555f417ba9753a76b3218b2/unliftio#monad-control">comparison section</a>:</p><blockquote><p><code>monad-control</code> allows us to unlift both styles. In theory, we could write a variant of <code>lifted-base</code> that never does state discards […] In other words, this is an advantage of <code>monad-control</code> over <code>MonadUnliftIO</code>. We've avoided providing any such extra typeclass in this package though, for two reasons:</p><ul><li><p><code>MonadUnliftIO</code> is a simple typeclass, easy to explain. We don't want to complicated [sic] matters […]</p></li><li><p>Having this kind of split would be confusing in user code, when suddenly [certain operations are] not available to us.</p></li></ul></blockquote><p>In other words, the authors of <code>unliftio</code> felt that <code>MonadBaseControl</code> was simply not worth the complexity, and they could get away with <code>MonadUnliftIO</code>. Frankly, if you feel the same way, by all means, use <code>unliftio</code>. I just found it too limiting given the way I write Haskell, plain and simple.</p><h2><a name="recap"></a>Recap</h2><p>So ends another long blog post. As often seems the case, I set out to write something short, but I ended up writing well over 5,000 words. I suppose that means I learned something from this experience, too: <code>MonadBaseControl</code> is more complicated than I had anticipated! Maybe there’s something to take away from that.</p><p>In any case, it’s over now, so I’d like to briefly summarize what we’ve covered:</p><ul><li><p><a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl"><code>MonadBaseControl</code></a> allows us to lift higher-order monadic operations.</p></li><li><p>It operates by capturing the current monadic state and explicitly threading it through the action in the base monad before restoring it.</p></li><li><p>That technique works well for polymorphic operations for the type <code>forall a. b a -&gt; b a</code>, but it can be tricky or even impossible for more complex operations, sometimes leading to discarded state.</p><p>This can sometimes be mitigated by restricting certain operations to stateless monads using a <code>StM m a ~ a</code> constraint, or by reimplementing the operation in terms of simpler primitives.</p></li><li><p>The <a href="http://hackage.haskell.org/package/lifted-base"><code>lifted-base</code></a> and <a href="http://hackage.haskell.org/package/lifted-async"><code>lifted-async</code></a> packages provide lifted versions of existing operations, avoiding the need to lift them yourself.</p></li></ul><p>As with many abstractions in Haskell, don’t worry too much if you don’t have a completely firm grasp of <code>MonadBaseControl</code> at first. Insight often comes with repeated experience, and <code>monad-control</code> can still be used in useful ways even without a perfect understanding. My hope is that this blog post has helped you build intuitions about <code>MonadBaseControl</code> even if some of the underlying machinery remains a little fuzzy, and I hope it can also serve as a reference for those who want or need to understand (or just be reminded of) all the little details.</p><p>Finally, I’ll admit <code>MonadBaseControl</code> isn’t especially elegant or beautiful as Haskell abstractions go. In fact, in many ways, it’s a bit of a kludge! Perhaps, in time, effect systems will evolve and mature so that it and its ilk are no longer necessary, and they may become distant relics of an inferior past. But in the meantime, it’s here, it’s useful, and I think it’s worth embracing. If you’ve shied away from it in the past, I hope I’ve illuminated it enough to make you consider giving it another try.</p><ol class="footnotes"><li id="footnote-0"><p>One example of a function with that type is <code>mask_</code>. <a href="#footnote-ref-0-1">↩</a></p></li><li id="footnote-1"><p>Types with polymorphic types under type constructors are called <em>impredicative</em>. GHC technically has limited support for impredicativity via the <code>ImpredicativeTypes</code> language extension, but as of GHC 8.8, it has been fairly broken for some time. A fix is apparently being worked on, but even if that effort is successful, I don’t know what impact it will have on type inference. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Note that <code>askRunInBase = liftBaseWith (pure . RunInBase)</code> does <em>not</em> typecheck, as it would require impredicative polymorphism: it would require instantiating the type of <code>(.)</code> with polymorphic types. The version using <code>($)</code> works because GHC actually has special typechecking rules for <code>($)</code>! Effectively, <code>f $ x</code> is really syntax in GHC. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Assume that <code>mask'</code> is a suitably lifted version of <code>mask</code> (which can in fact be made state-preserving). <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Defeating Racket’s separate compilation guarantee2019-04-21T00:00:00Z2019-04-21T00:00:00ZAlexis King<article><p>Being a self-described <a href="https://felleisen.org/matthias/manifesto/sec_pl-pl.html">programming-language programming language</a> is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate <a href="https://www.cs.utah.edu/plt/publications/macromod.pdf">composable and compilable macros</a>. One of the module system’s foundational properties is its <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29"><em>separate compilation guarantee</em></a>, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is <em>essential</em> for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.</p><p>Yet today, in this blog post, we’re going to do exactly that.</p><h2><a name="what-is-the-separate-compilation-guarantee"></a>What is the separate compilation guarantee?</h2><p>Before we get to the fun part (i.e. breaking things), let’s go over some fundamentals so we understand what we’re breaking. The authoritative source for the separate compilation guarantee is <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">the Racket reference</a>, but it is dense, as authoritative sources tend to be. Although I enjoy reading technical manuals for sport, it is my understanding that not all the people who read this blog are as strange as I am, so let’s start with a quick primer, instead. (If you’re already an expert, feel free to <a href="#section:main-start">skip to the next section</a>.)</p><p>Racket is a macro-enabled programming language. In Racket, a macro is a user-defined, code-to-code transformation that occurs at compile-time. These transformations cannot make arbitrary changes to the program—in Racket, they are usually required to be <em>local</em>, affecting a single expression or definition at a time—but they may be implemented using arbitrary code. This means that a macro can, if it so desires, read the SSH keys off your filesystem and issue an HTTP request to send them someplace.</p><p>That kind of attack is bad, admittedly, but it’s also <em>uninteresting</em>: Racket allows you do all that and then some, making no attempt to prevent it.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Racket calls these “external effects,” things that affect state outside of the programming language. They sound scary, but in practice, <em>internal effects</em>—effects that mutate state inside the programming language—are a much bigger obstacle to practical programming. Let’s take a look at why.</p><p>Let’s say we have a module with some global, mutable state. Perhaps it is used to keep track of a set of delicious foods:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><p>Using this interface, let’s write a program that checks if a particular food, given as a command-line argument, is delicious:</p><pre><code class="pygments"><span class="c1">;; check-food.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"pineapple"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"sushi"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"cheesecake"</span><span class="p">)</span> + +<span class="p">(</span><span class="k">command-line</span> +<span class="w"> </span><span class="kd">#:args</span><span class="w"> </span><span class="p">[</span><span class="n">food-to-check</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is not delicious.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>cheesecake +cheesecake<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>licorice +licorice<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>Exhilarating. (Sorry, licorice fans.) But what if a <em>macro</em> were to call <code>add-delicious-food!</code>? What would happen? For example, what if we wrote a macro to add a lot of foods at once?<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="n">fst:string</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd:string</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">for*</span><span class="w"> </span><span class="p">([</span><span class="n">fst-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">fst</span><span class="w"> </span><span class="k">...</span><span class="p">]))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">snd</span><span class="w"> </span><span class="k">...</span><span class="p">]))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="p">(</span><span class="nb">string-append</span><span class="w"> </span><span class="n">fst-str</span><span class="w"> </span><span class="s2">" "</span><span class="w"> </span><span class="n">snd-str</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">))</span> + +<span class="c1">; should add “fried chicken,” “roasted chicken”, “fried potato,” and “roasted potato”</span> +<span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="s2">"fried"</span><span class="w"> </span><span class="s2">"roasted"</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="s2">"chicken"</span><span class="w"> </span><span class="s2">"potato"</span><span class="p">])</span></code></pre><p>Now, what do you think executing <code>racket check-food.rkt 'fried chicken'</code> will do?</p><p>Clearly, the program should print <code>fried chicken is a delicious food</code>, and indeed, many traditional Lisp systems would happily produce such a result. After all, running <code>racket check-food.rkt 'fried chicken'</code> must load the source code inside <code>check-food.rkt</code>, expand and compile it, then run the result. While the program is being expanded, the compile-time calls to <code>add-delicious-food!</code> should add new elements to the <code>delicious-food</code> set, so when the program is executed, the string <code>"fried chicken"</code> ought to be in it.</p><p>But if you actually try this yourself, you will find that <em>isn’t</em> what happens. Instead, Racket rejects the program:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +check-food.rkt:12:11:<span class="w"> </span>add-delicious-food!:<span class="w"> </span>reference<span class="w"> </span>to<span class="w"> </span>an<span class="w"> </span>unbound<span class="w"> </span>identifier +<span class="w"> </span>at<span class="w"> </span>phase:<span class="w"> </span><span class="m">1</span><span class="p">;</span><span class="w"> </span>the<span class="w"> </span>transformer<span class="w"> </span>environment +<span class="w"> </span><span class="k">in</span>:<span class="w"> </span>add-delicious-food!</code></pre><p>Why does Racket reject this program? Well, consider that Racket allows programs to be pre-compiled using <code>raco make</code>, doing all the work of macroexpansion and compilation to bytecode ahead of time. Subsequent runs of the program will use the pre-compiled version, without having to run all the macros again. This is a problem, since expanding the <code>add-food-combinations!</code> macro had side-effects that our program depended on!</p><p>If Racket allowed the above program, it might do different things depending on whether it was pre-compiled. Running directly from source code might treat <code>'fried chicken'</code> as a delicious food, while running from pre-compiled bytecode might not. Racket considers this unacceptable, so it disallows the program entirely.</p><h3><a name="preserving-separate-compilation-via-phases"></a>Preserving separate compilation via phases</h3><p>Hopefully, you are now mostly convinced that the above program is a bad one, but you might have some lingering doubts. You might, for example, wonder if Racket disallows mutable compile-time state entirely. That is not the case—Racket really does allow everything that happens at runtime to happen at compile-time—but it does prevent compile-time and run-time state from ever <em>interacting</em>. Racket stratifies every program into a compile-time part and a run-time part, and it restricts communication between them to limited, well-defined channels (mainly via expanding to code that does something at run-time).</p><p>Racket calls this system of stratification <em>phases</em>. Code that executes at run-time belongs to the <em>run-time phase</em>, while code that executes at compile-time (i.e. macros) belongs to the <em>compile-time phase</em>. When a variable is defined, it is always defined in a particular phase, so bindings declared with <code>define</code> can only be used at run-time, while bindings declared with <code>define-for-syntax</code> can only be used at compile-time. Since <code>add-delicious-food!</code> was declared using <code>define</code>, it was not allowed (and in fact was not even visible) in the body of the <code>add-food-combinations!</code> macro.</p><p>While the whole macro system could work precisely as just described, such a strict stratification would be incredibly rigid. Since every definition would belong to either run-time or compile-time, but never both, reusing run-time code to implement macros would be impossible. While the example in the previous section might make it seem like that’s a good thing, it very often isn’t: imagine if general-purpose functions like <code>map</code> and <code>filter</code> all needed to be written twice!</p><p>To avoid this problem, Racket allows modules to be imported at both run-time and compile-time, so long as it’s done explicitly. Writing <code>(require "some-library.rkt")</code> requires <code>some-library.rkt</code> for run-time code, but writing <code>(require (for-syntax "some-library.rkt"))</code> requires it for compile-time code. Requiring a module <code>for-syntax</code> is sort of like implicitly adjusting all of its uses of <code>define</code> to be <code>define-for-syntax</code>, instead, effectively shifting all the code from run-time to compile-time. This kind of operation is therefore known as <em>phase shifting</em> in Racket terminology.</p><p>We can use phase shifting to make the program we wrote compile. If we adjust the <code>require</code> at the beginning of our program, then we can ensure <code>add-delicious-food!</code> is visible to both the run-time and compile-time parts of <code>check-food.rkt</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">))</span></code></pre><p>Now our program compiles. However, if you’ve been following everything carefully, you should be wondering why! According to the last section, sharing state between run-time and compile-time fundamentally can’t work without introducing inconsistencies between uncompiled and pre-compiled code. And that’s true—such a thing would cause all sorts of problems, and Racket doesn’t allow it. If you run the program, whether pre-compiled or not, you’ll find it always does the same thing:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>This seems rather confusing. What happened to the calls to <code>add-delicious-food!</code> inside our <code>add-food-combinations!</code> macro? If we stick a <code>printf</code> inside <code>add-delicious-food!</code>, we’ll find that it really does get called:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"Registering ~a as a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And in fact, if we pre-compile <code>check-food.rkt</code>, we’ll see that the first four registrations appear at compile-time, exactly as we expect:</p><pre><code class="pygments">$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>The compile-time registrations really are happening, but Racket is automatically restricting the compile-time side-effects so they only apply at compile-time. After compilation has finished, Racket ensures that compile-time side effects are thrown away, and the run-time code starts over with fresh, untouched state. This guarantees consistent behavior, since it becomes impossible to distinguish at run-time whether a module was just compiled on the fly, or if it was pre-compiled long ago (possibly even on someone else’s computer).</p><p>This is the essence of the separate compilation guarantee. To summarize:</p><ul><li><p>Run-time and compile-time are distinct <em>phases</em> of execution, which cannot interact.</p></li><li><p>Modules can be required at multiple phases via <em>phase shifting</em>, but their state is kept separate. Each phase gets its own copy of the state.</p></li><li><p>Ensuring that the state is kept separate ensures predictable program behavior, no matter when the program is compiled.</p></li></ul><p>This summary is a simplification of phases in Racket. The full Racket module system does not have only two phases, since macros can also be <em>used</em> at compile-time to implement other macros, effectively creating a separate “compile-time” for the compile-time code. Each compile-time pass is isolated to its own phase, creating a finite but arbitrarily large number of distinct program phases (all but one of which occur at compile-time).</p><p>Furthermore, the separate compilation guarantee does not just isolate the state of each phase from the state of other phases but also isolates all compile-time state from the compile-time state of other modules. This ensures that compilation is still deterministic even if modules are compiled in a different <em>order</em>, or if several modules are sometimes compiled individually while other times compiled together all at once.</p><p>If you want to learn more, the full details of the module system are described at length in the <a href="https://docs.racket-lang.org/guide/phases.html">General Phase Levels</a> section of the Racket Guide, but the abridged summary I’ve described is enough for the purposes of this blog post. If the bulleted list above mostly made sense to you, you’re ready to move on.</p><h2 id="section:main-start">How we’re going to break it</h2><p>The separate compilation guarantee is a sturdy opponent, but it is not without weaknesses. Although no API in Racket, safe or unsafe, allows arbitrarily disabling phase separation, a couple features of Racket are already known to allow limited forms of cross-phase communication.</p><p>The most significant of these, and the one we’ll be using as our vector of attack, is the logger. Unlike many logging systems, which are exclusively string-oriented, Racket’s logging interface allows structured logging by associating an arbitrary Racket value with each and every log message. Since it is possible to set up listeners within Racket that receive log messages sent to a particular “topic,” the logger can be used as a communication channel to send values between different parts of a program.</p><p>The following program illustrates how this works. One thread creates a listener for all log messages on the topic <code>'send-me-a-value</code> using <code>make-log-receiver</code>, then uses <code>sync</code> to block until a value is received. Meanwhile, a second thread sends values through the logger using <code>log-message</code>. Together, this creates a makeshift buffered, asynchronous channel:</p><pre><code class="pygments"><span class="c1">;; log-comm.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span><span class="w"> </span><span class="c1">; wait forever</span></code></pre><pre><code>$ racket log-comm.rkt +'#(debug "" 1 send-me-a-value) +'#(debug "" 2 send-me-a-value) +'#(debug "" 3 send-me-a-value) +'#(debug "" 4 send-me-a-value) +^Cuser break +</code></pre><p>In this program, the value being sent through the logger is just a number, which isn’t very interesting. But the value really can be <em>any</em> value, even arbitrary closures or mutable data structures. It’s even possible to send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> through a logger, which can subsequently be used to communicate directly, without having to abuse the logger.</p><p>Generally, this feature of loggers isn’t very useful, since Racket has plenty of features for cross-thread communication. What’s special about the logger, however, is that it is global, and it is cross-phase.</p><p>The cross-phase nature of the logger makes some sense. If a Racket program creates a namespace (that is, a fresh environment for dynamic evaluation), then uses it to expand and compile a Racket module, the process of compilation might produce some log messages, and the calling thread might wish to receive them. It wouldn’t be a very useful logging system if log messages during compile-time were always lost. However, this convenience is a loophole in the phase separation system, since it allows values to flow—bidirectionally—between phases.</p><p>This concept forms the foundation of our exploit, but it alone is not a new technique, and I did not discover it. However, all existing uses I know of that use the logger for cross-phase communication require control of the parent namespace in which modules are being compiled, which means some code must exist “outside” the actual program. That technique does not work for ordinary programs run directly with <code>racket</code> or compiled directly with <code>raco make</code>, so to get there, we’ll need something more clever.</p><h3><a name="the-challenge"></a>The challenge</h3><p>Our goal, therefore, is to share state between phases <em>without</em> controlling the compilation namespace. More precisely, we want to be able to create an arbitrary module-level definition that is <em>cross-phase persistent</em>, which means it will be evaluated once and only once no matter how many times its enclosing module is re-instantiated (i.e. given a fresh, untouched state) at various phases. A phase-shifted <code>require</code> of the module that contains the definition should share state with an unshifted version of the module, breaking the separate compilation guarantee wide open.</p><p>To use the example from the previous section, we should be able to adjust <code>foods.rkt</code> very slightly…</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="c1">; share across phases</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="cm">#| ... |#</span></code></pre><p>…and the <code>delicious-foods</code> mutable state should magically become cross-phase persistent. When running <code>check-food.rkt</code> from source, we should see the side-effects persisted from the module’s compilation, while running from pre-compiled bytecode should give us the result with compile-time effects discarded.</p><p>We already know the logger is going to be part of our exploit, but implementing <code>define/cross-phase</code> on top of it is more subtle than it might seem. In our previous example that used <code>make-log-receiver</code>, we had well-defined sender and receiver threads, but who is the “sender” in our multi-phased world? And what exactly is the sender sending?</p><p>To answer those questions, allow me to outline the general idea of our approach:</p><ol><li><p>The first time our <code>foods.rkt</code> module is instantiated, at any phase, it evaluates the <code>(mutable-set)</code> expression to produce a new mutable set. It spawns a sender thread that sends this value via the logger to anyone who will listen, and that thread lingers in the background for the remaining duration of the program.</p></li><li><p>All subsequent instantiations of <code>foods.rkt</code> do <em>not</em> evaluate the <code>(mutable-set)</code> expression. Instead, they obtain the existing set by creating a log receiver and obtaining the value the sender thread is broadcasting. This ensures that a single value is shared across all instantiations of the module.</p></li></ol><p>This sounds deceptively simple, but the crux of the problem is how to determine whether <code>foods.rkt</code> has previously been instantiated or not. Since we can only communicate across phases via the logger, we cannot use any shared state to directly record the first time the module is instantiated. We can listen to a log receiver and wait to see if we get a response, but this introduces a race condition: how long do we wait until giving up and deciding we’re the first instantiation? Worse, what if two threads instantiate the module at the same time, and both threads end up spawning a new sender thread, duplicating the state?</p><p>The true challenge, therefore, is to develop a protocol by which we can be <em>certain</em> we are the first instantiation of a module, without relying on any unspecified behavior, and without introducing any race conditions. This is possible, but it isn’t obvious, and it requires combining loggers with some extra tools available to the Racket programmer.</p><h3><a name="the-key-idea"></a>The key idea</h3><p>It’s finally time to tackle the key idea at the heart of our exploit: garbage collection. In Racket, garbage collection is an observable effect, since Racket allows attaching finalizers to values via <a href="https://docs.racket-lang.org/reference/willexecutor.html">wills and executors</a>. Since a single heap is necessarily shared by the entire VM, behavior happening on other threads (even in other phases) can be indirectly observed by creating a unique value—a “canary”—then sending it to another thread, and waiting to see if it will be garbage collected or not (that is, whether or not the canary “dies”).</p><p>Remember that logs and log receivers are effectively buffered, multicast, asynchronous FIFO channels. Since they are buffered, if any thread is already listening to a logger topic when a value is sent, it cannot possibly be garbage collected until that thread either reads it and discards it or the receiver itself is garbage collected. It’s possible to use this mechanism to observe whether or not another thread is already listening on a topic, as the following program demonstrates:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">executor</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><pre><code>$ racket check-receivers.rkt +no receivers for 'bar +receiver exists for 'foo +</code></pre><p>However, this program has some problems. For one, it needs to call <code>collect-garbage</code> several times to be certain that the canary will be collected if there are no listeners, which can take a second or two, and it also assumes that three calls to <code>collect-garbage</code> will be enough to collect the canary, though there is no guarantee that will be true.</p><p>A bulletproof solution should be both reasonably performant and guaranteed to work. To get there, we have to combine this idea with something more. Here’s the trick: instead of sending the canary alone, send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> alongside it. Synchronize on both the canary’s executor <em>and</em> the channel so that the thread will unblock if either the canary is collected <em>or</em> the channel is received and sent a value using <code>channel-put</code>. Have the receiver listen for the channel on a separate thread, and when it receives it, send a value back to unblock the waiting thread as quickly as possible, without needing to rely on a timeout or a particular number of calls to <code>collect-garbage</code>.</p><p>Using that idea, we can revise the program:</p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="c1">; send the channel + the canary</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">canary</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span><span class="w"> </span><span class="c1">; yield to try to let the receiver thread work</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">received</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">)))</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">received</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span><span class="w"> </span><span class="c1">; collect garbage and try again</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> +<span class="p">(</span><span class="nb">void</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="k">_</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><p>Now the program completes almost instantly. For this simple program, the explicit <code>(sleep)</code> call is effective enough at yielding that, on my machine, <code>(check-receivers 'foo)</code> returns without ever calling <code>collect-garbage</code>, and <code>(check-receivers 'bar)</code> returns after performing a single minor collection.</p><p>This is extremely close to a bulletproof solution, but there are two remaining subtle issues:</p><ol><li><p>There is technically a race condition between the <code>(sync recv)</code> in the receiver thread and the subsequent <code>channel-put</code>, since it’s possible for the canary to be received, discarded, and garbage collected before reaching the call to <code>channel-put</code>, which the sending thread would incorrectly interpret as indicating the topic has no receivers.</p><p>To fix that, the receiver thread can send the canary itself back through the channel, which fundamentally has to work, since the value cannot be collected until it has been received by the sending thread, at which point the <code>sync</code> has already chosen the channel.</p></li><li><p>It is possible for the receiver thread to receive the log message and call <code>channel-put</code>, but for the sending thread to somehow die in the meantime (which cannot be protected against in general in Racket, since <code>thread-kill</code> immediately and forcibly terminates a thread). If this were to happen, the sending thread would never obtain the value from the channel, blocking the receiving thread indefinitely.</p><p>A solution is to spawn a new thread for each <code>channel-put</code> instead of calling it directly from the receiving thread. Conveniently, this both ensures the receiving thread never gets stuck and avoids resource leaks, since the Racket runtime is smart enough to GC a thread blocked on a channel that has no other references and therefore can never be unblocked.</p></li></ol><p>With those fixes in place, the program is, to the best of my knowledge, bulletproof. It will always correctly determine whether or not a logger has a listener, with no race conditions or reliance upon unspecified behavior of the Racket runtime. It does, however, make a couple of assumptions.</p><p>First, it assumes that the value of <code>(current-logger)</code> is shared between the threads. It is true that <code>(current-logger)</code> can be changed, and sometimes is, but it’s usually done via <code>parameterize</code>, not mutation of the parameter directly. Therefore, this can largely be mitigated by storing the value of <code>(current-logger)</code> at module instantiation time.</p><p>Second, it assumes that no other receivers are listening on the same topic. Technically, even using a unique, uninterned key for the topic is insufficient to ensure that no receivers are listening to it, since a receiver can choose to listen to all topics. However, in practice, it is highly unlikely that any receiver would willfully choose to listen to all topics at the <code>'debug</code> level, since the receiver would be inundated with enormous amounts of useless information. Even if such a receiver were to be created, it is highly likely that it would dequeue the messages as quickly as possible and discard the accompanying payload, since doing otherwise would cause all messages to be retained in memory, leading to a significant memory leak.</p><p>Both these problems can be mitigated by using a logger other than the root logger, which is easy in this example. However, for the purpose of subverting the separate compilation guarantee, we would have no way to share the logger object itself across phases, defeating the whole purpose, so we are forced to use the root logger and hope the above two assumptions remain true (but they usually do).</p><h3><a name="preparing-the-exploit"></a>Preparing the exploit</h3><p>If you’ve made it here, congratulations! The most difficult part of this blog post is over. All that’s left is the fun part: performing the exploit.</p><p>The bulk of our implementation is a slightly adapted version of <code>check-receivers</code>:</p><pre><code class="pygments"><span class="c1">;; define-cross-phase.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="k">thunk</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="k">==</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="nb">eq?</span><span class="p">))</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">execute-evt</span><span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="nb">will-execute</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">execute-evt</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">result</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">value</span><span class="p">)</span> +<span class="w"> </span><span class="n">value</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="p">(</span><span class="k">thunk</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">value</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)]))))</span> +<span class="w"> </span><span class="n">value</span><span class="p">]))</span></code></pre><p>There are a few minor differences, which I’ll list:</p><ol><li><p>The most obvious difference is that <code>make-cross-phase</code> does the work of both checking if a receiver exists—which I’ll call the <em>manager thread</em>—and spawning it if it doesn’t. If it does end up spawning a manager thread, it evaluates the given thunk to produce a value, which becomes the cross-phase value that will be sent through the channel alongside the canary.</p></li><li><p>Once the manager thread is created, subsequent calls to <code>make-cross-phase</code> will receive the value through the channel and return it instead of re-invoking <code>thunk</code>. This is what ensures the right-hand side of each use of <code>define/cross-phase</code> is only ever evaluated once.</p></li><li><p>Since <code>make-cross-phase</code> needs to create a log receiver if no manager thread exists, it does so immediately, before sending the canary through the logger. This avoids a race condition between multiple threads that are simultaneously competing to become the manager thread, where both threads could send a canary through the logger before either was listening, both canaries would get GC’d, and both threads would spawn a new manager.<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><p>Creating the receiver before sending the canary avoids this problem, but the thread now needs to receive its own canary and discard it before synchronizing on the channel and executor, since otherwise it will retain a reference to the canary. It’s possible that in between creating the receiver and sending the canary, another thread also sent a canary, so it needs to drop any log messages it finds that don’t include its own canary.</p><p>This ends up working out perfectly, since every thread drops all the messages received before the one containing its own canary, but retains all subsequent values. This means that only one thread can ever “win” and become the manager, since the first thread to send a canary is guaranteed to retain all subsequent canaries, yet also guaranteed its canary will be GC’d. Other threads racing to become the manager will remain blocked until the manager thread is created, since its canaries will be retained by the manager-to-be until it dequeues them.</p><p>(This is the most subtle part of the process to get right, but conveniently, it mostly just works out without very much code. If you didn’t understand any of the above three paragraphs, it isn’t a big deal.)</p></li></ol><p>The final piece to this puzzle is to define the <code>define/cross-phase</code> macro that wraps <code>make-cross-phase</code>. The macro is actually slightly more involved than just generating a call to <code>make-cross-phase</code> directly, since we’d like to use an uninterned symbol for the topic instead of an interned one, just to ensure it is unique. Ordinarily, this might seem impossible, since an uninterned symbol is fundamentally a unique value that needs to be communicated across phases, and the whole problem we are solving is creating a communication channel that spans phases. However, Racket actually provides some built-in support for sharing uninterned symbols across phases (plus some other kinds of values, but they must always be immutable). To do this, we need to generate a <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._cross-phase._persistent-modules%29">cross-phase persistent submodule</a> that exports an uninterned symbol, then pass that symbol as the topic to <code>make-cross-phase</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">define/cross-phase</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">cross-phase-topic-key</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">#%kernel</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%declare</span><span class="w"> </span><span class="kd">#:cross-phase-persistent</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%provide</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">topic</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="s2">"cross-phase"</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">topic-mod-name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">e</span><span class="p">)))))</span></code></pre><p>And that’s really it. We’re done.</p><h3><a name="executing-the-exploit"></a>Executing the exploit</h3><p>With our implementation of <code>define/cross-phase</code> in hand, all that’s left to do is run our original <code>check-foods.rkt</code> program and see what happens:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +set-add!:<span class="w"> </span>contract<span class="w"> </span>violation: +expected:<span class="w"> </span>set? +given:<span class="w"> </span><span class="o">(</span>mutable-set<span class="w"> </span><span class="s2">"fried chicken"</span><span class="w"> </span><span class="s2">"roasted chicken"</span><span class="w"> </span><span class="s2">"roasted potato"</span><span class="w"> </span><span class="s2">"fried potato"</span><span class="o">)</span> +argument<span class="w"> </span>position:<span class="w"> </span>1st +other<span class="w"> </span>arguments...: +<span class="w"> </span>x:<span class="w"> </span><span class="s2">"pineapple"</span></code></pre><p>Well, I don’t know what you expected. Play stupid games, win stupid prizes.</p><p>This error actually makes sense, but it belies one reason (of many) why this whole endeavor is probably a bad idea. Although we’ve managed to make our mutable set cross-phase persistent, our references to set operations like <code>set-add!</code> and <code>set-member?</code> are not, and every time <code>racket/set</code> is instantiated in a fresh phase, it creates an entirely new instance of the <code>set</code> structure type. This means that even though we have a bona fide mutable set, it isn’t actually the type of set that this phase’s <code>set-add!</code> understands!</p><p>Of course, this isn’t a problem that some liberal application of <code>define/cross-phase</code> can’t solve:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-member?</span><span class="w"> </span><span class="nb">set-member?</span><span class="p">)</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-add!</span><span class="w"> </span><span class="nb">set-add!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And thus we find that another so-called “guarantee” isn’t.</p><h2><a name="reflection"></a>Reflection</h2><p>Now comes the time in the blog post when I have to step back and think about what I’ve done. Have mercy.</p><p>Everything in this blog post is a terrible idea. No, you should not use loggers for anything that isn’t logging, you shouldn’t use wills and executors for critical control flow, and obviously you should absolutely not intentionally break one of the most helpful guarantees the Racket module system affords you.</p><p>But I thought it was fun to do all that, anyway.</p><p>The meaningful takeaways from this blog post aren’t that the separate compilation guarantee can be broken, nor that any of the particular techniques I used hold, but that</p><ol><li><p>ensuring non-trivial guarantees is really hard,</p></li><li><p>despite that, the separate compilation guarantee is really, really hard to break,</p></li><li><p>the separate compilation guarantee is good, and you should appreciate the luxury it affords you while writing Racket macros,</p></li><li><p>avoiding races in a concurrent environment can be extremely subtle,</p></li><li><p>and Racket is totally <em>awesome</em> for giving me this much rope to hang myself with.</p></li></ol><p>If you want to hang yourself with Racket, too, <a href="https://gist.github.com/lexi-lambda/f173a84fc9727977bcea657b3bb0cd4f">runnable code from this blog post is available here</a>.</p><ol class="footnotes"><li id="footnote-1"><p>This isn’t <em>strictly</em> true, since Racket provides sandboxing mechanisms that can compile and execute untrusted code without file system or network access, but this is not the default compilation mode. Usually, it doesn’t matter nearly as much as it might sound: most of the time, if you’re compiling untrusted code, you’re also going to run it, and running untrusted code can do all those things, anyway. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>This is actually a <em>terrible</em> use case for a macro, since an ordinary function would do just fine, but I’m simplifying a little to keep the example small. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Racket actually provides this functionality directly via the <code>log-level?</code> procedure. However, since <code>log-level?</code> provides no way to determine how <em>many</em> receivers are listening to a topic, using it to guard against creating a receiver is vulnerable to a race condition that the garbage collection-based approach can avoid, as is discussed later. Furthermore, the GC technique is more likely to be resilient to nosy log receivers listening on all topics at the <code>'debug</code> level, since they will almost certainly dequeue and discard the value quickly (as otherwise they would leak large quantities of memory). <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>This race is the one that makes using <code>log-level?</code> untenable, since the receiver needs to be created before the topic is checked for listeners to avoid the race, which can’t be done with <code>log-level?</code> (since it would always return <code>#t</code>). <a href="#footnote-ref-4-1">↩</a></p></li></ol></article>Macroexpand anywhere with local-apply-transformer!2018-10-06T00:00:00Z2018-10-06T00:00:00ZAlexis King<article><p>Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only <em>certain positions</em> in Racket code are subject to macroexpansion.</p><p>To illustrate, consider a macro that provides a Clojure-style <code>let</code> form:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This can be used anywhere an expression is expected, and it does as one would expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>However, a novice macro programmer might realize that <code>clj-let</code> really only modifies the syntax of <em>binding pairs</em> for a <code>let</code> form. Therefore, could one define a macro that only adjusts the binding pairs of some existing <code>let</code> form instead of expanding to an entire <code>let</code>? That is, could one write the above example like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>The answer is <em>no</em>: the binding pairs of a <code>let</code> form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion <em>anywhere</em> in a Racket program.</p><h2><a name="why-only-some-positions-are-subject-to-macroexpansion"></a>Why only some positions are subject to macroexpansion</h2><p>To understand <em>why</em> the macroexpander refuses to touch certain positions in a program, we must first understand how the macro system operates. In Racket, a macro is defined as a compile-time function associated with a particular binding, and macros are given complete control over the syntax trees they are surrounded with. If we define a macro <em><code>mac</code></em>, then we write the expression <code>(<em>mac</em> <em>form</em>)</code>, <em><code>form</code></em> is provided as-is to <em><code>mac</code></em> as a syntax object. Its structure can be anything at all, since <em><code>mac</code></em> can be an arbitrary Racket function, and that function can use <em><code>form</code></em> however it pleases.</p><p>To give a concrete illustration, consider a macro that binds some identifiers to symbols in a local scope:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">x</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="ss">hello</span><span class="w"> </span><span class="ss">goodbye</span><span class="p">)</span></code></pre><p>It isn’t the most exciting macro in the world, but it illustrates a key point: the first subform to <code>let-symbols</code> is a list of identifiers that are eventually put in <em>binding</em> position. This means that <code>hello</code> and <code>goodbye</code> are bindings, not uses, and such bindings shadow any existing bindings that might have been in scope:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">foo</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> +<span class="w"> </span><span class="n">foo</span><span class="p">))</span> +<span class="o">&#39;</span><span class="ss">foo</span></code></pre><p>This might not seem very interesting, but it’s critical to understand, since it means that the expander <em>can’t know</em> which sub-pieces of a use of <code>let-symbols</code> will eventually be expressions themselves until it expands the macro and discovers it produces a <code>let</code> form, so it can’t know where it’s safe to perform macroexpansion. To make this more explicit, imagine we define a macro under some name, then try and use that name with our <code>let-symbols</code> macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">x:id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="n">hello</span><span class="p">)</span></code></pre><p>What should the above program do? If we treat the first use of <code>hello</code> in the <code>let-symbols</code> form as a macro application, then <code>(hello goodbye)</code> should be transformed into <code>(goodbye)</code>, and the use of <code>hello</code> in the body should be a syntax error. But if the first use of <code>hello</code> was instead intended to be a binder, then it should shadow the <code>hello</code> definition above, and the output of the program should be <code>'hello</code>.</p><p>To avoid the chaos that would ensue if defining a macro could completely break local reasoning about other macros, Racket chooses the second option, and the program produces <code>'hello</code>. The macroexpander has no way of knowing <em>how</em> each macro will inspect its constituent pieces, so it avoids touching anything until the macro expands. After it discovers the <code>let</code> form in the expansion of <code>let-symbols</code>, it can safely determine that the body expressions are, indeed, expressions, and it can recursively expand any macros they contain. To put things another way, a macro’s sub-forms are never expanded before the macro itself is expanded, only after.</p><h2><a name="forcing-sub-form-expansion"></a>Forcing sub-form expansion</h2><p>The above section explains why the expander must operate as it does, but it’s a little bit unsatisfying. What if we write a macro where we <em>want</em> certain sub-forms to be expanded before they are passed to us? Fortunately, the Racket macro system provides an API to handle this use case, too.</p><p>It is true that the Racket macro system never <em>automatically</em> expands sub-forms before outer forms are expanded, but macro transformers can explicitly op-in to recursive expansion via the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a> function. This function effectively yields control back to the expander to expand some arbitrary piece of syntax as an expression, and when it returns, the macro transformer can inspect the expanded expression however it wishes. In theory, this can be used to implement extensible macros that allow macroexpansion in locations other than expression position.</p><p>To give an example of such a macro, consider the Racket <code>match</code> form, which implements an expressive pattern-matcher as a macro. One of the most interesting qualities of Racket’s <code>match</code> macro is that its pattern language is user-extensible, essentially allowing pattern-level macros. For example, a user might find they frequently match against natural numbers, and they wish to be able to write <code>(nat n)</code> as a shorthand for <code>(? exact-nonnegative-integer? n)</code>. Fortunately, this is easy using <code>define-match-expander</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-match-expander</span><span class="w"> </span><span class="n">nat</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">pat</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">?</span><span class="w"> </span><span class="nb">exact-nonnegative-integer?</span><span class="w"> </span><span class="n">pat</span><span class="p">)]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="mi">-5</span><span class="w"> </span><span class="mi">-2</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">-7</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">list</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">nat</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">n</span><span class="p">])</span> +<span class="mi">4</span></code></pre><p>Clearly, <code>match</code> is somehow expanding the <code>nat</code> match expander as a part of its expansion. Is it using <code>local-expand</code>?</p><p>Well, no. While <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">a previous blog post of mine</a> has illustrated that it is possible to do such a thing with <code>local-expand</code> via some clever trickery, <code>local-expand</code> is really designed to expand <em>expressions</em>. This is a problem, since <code>(nat n)</code> is not an expression, it’s a pattern: it will expand into <code>(? exact-nonnegative-integer? n)</code>, which will lead to a syntax error, since <code>?</code> is not bound in the world of expressions. Instead, for a long while, <code>match</code> and forms like it have emulated how the expander performs macroexpansion in ad-hoc ways. Fortunately, as of Racket v7.0, the new <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Fapply-transformer..rkt%29._local-apply-transformer%29%29"><code>local-apply-transformer</code></a> API provides a way to invoke recursive macroexpansion in a consistent way, and it doesn’t assume that what’s being expanded is an expression.</p><h3><a name="a-closer-look-at-local-apply-transformer"></a>A closer look at <code>local-apply-transformer</code></h3><p>If <code>local-apply-transformer</code> is the answer, what does it actually do? Well, <code>local-apply-transformer</code> allows explicitly invoking a transformer function on some piece of syntax and retrieving the result. In other words, <code>local-apply-transformer</code> allows expanding an arbitrary macro, but since it doesn’t make any assumptions about what the output will be, it only expands it <em>once</em>: just a single step of macro transformation.</p><p>To illustrate, we can write a macro that uses <code>local-apply-transformer</code> to invoke a transformer function and preserve the result using <code>quote-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/apply-transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define-for-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>When we use <code>mac</code>, our <code>flip</code> function will be applied, as a macro, to the syntax object we provide:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="nb">&gt;</span></code></pre><p>Alright, so this works, but it raises some questions. Why is <code>flip</code> defined as a function at phase 1 (using <code>define-for-syntax</code>) instead of as a macro (using <code>define-syntax</code>)? What’s the deal with the <code>'expression</code> argument to <code>local-apply-transformer</code> given that <code>local-apply-transformer</code> is supposedly decoupled from expression expansion? And finally, how is this any different from just calling our <code>flip</code> function on the syntax object directly by writing <code>(flip #'(([x 1]) let x))</code>?</p><p>Let’s start with the first of those questions: why is <code>flip</code> defined as a function rather than as a macro? Well, <code>local-apply-transformer</code> is a fairly low-level operation: remember, it doesn’t assume <em>anything</em> about the argument it’s given! Therefore, it doesn’t take an expression containing a macro and expand it based on its structure, it needs to be explicitly provided the macro transformer function to apply. In practice, this might not seem very useful, since presumably we want to write our macros as macros, not as phase 1 functions. Fortunately, it’s possible to look up the function associated with a macro binding using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, so if we use that, we can define <code>flip</code> using <code>define-syntax</code> as usual:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>Now for the next question: what is the meaning of the <code>'expression</code> argument? This one is more of a historical artifact than anything else: when the expander applies a macro transformer, it does it in a “context”, which is accessible using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-context%29%29"><code>syntax-local-context</code></a> function. This context can be one of a predefined enumeration of cases, including <code>'expression</code>, <code>'top-level</code>, <code>'module</code>, <code>'module-begin</code>, or a list representing a definition context. Whether or not any of those actually apply to our use case, we still have to pick one, but aside from how they affect the value returned by <code>syntax-local-context</code> (which some macros inspect), the value we choose is largely irrelevant. Using <code>'expression</code> will do, even if it’s a bit of a lie.</p><p>Finally, how does any of this differ from just applying the function we get directly? Well, the critical answer is all about <em>hygiene</em>. Racket’s macro system is hygienic, which, among other things, ensures bindings defined with the same name in different places do not unintentionally conflict. Racket’s hygiene mechanism is implemented in the macroexpander, when macro transformers are applied. If we just applied the <code>flip</code> transformer procedure to a syntax object directly, we would circumvent this hygiene mechanism, potentially causing all sorts of problems. By using <code>local-apply-transformer</code>, we ensure hygiene is preserved.</p><p>There is one small problem left with our program, however. Can you spot it? The key is to consider what would happen if we used <code>flip</code> as an ordinary macro, without using <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="n">let:</span><span class="w"> </span><span class="n">bad</span><span class="w"> </span><span class="k">syntax</span> +<span class="w"> </span><span class="n">in:</span><span class="w"> </span><span class="k">let</span></code></pre><p>What happened? Well, remember that when a macro in Racket is used, it receives the whole use site as a syntax object: in this case, <code>#'(flip (([x 1]) let x))</code>. This means that <code>flip</code> ought to be written to parse its argument slightly differently:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span></code></pre><p>Indeed, now that we’ve properly restructured the macro, we can easily switch to using the convenient <code>define-simple-macro</code> shorthand:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This means we also need to update our definition of <code>mac</code> to provide the full syntax object the expander would:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>This might seem redundant, but remember, <code>local-apply-transformer</code> is very low-level! While the convention that <code>(<em>mac</em> . _)</code> is the syntax for a macro transformation might seem obvious, <code>local-apply-transformer</code> makes no assumptions. It just does what we tell it to do.</p><h3><a name="applying-local-apply-transformer"></a>Applying <code>local-apply-transformer</code></h3><p>So what does <code>local-apply-transformer</code> have to do with the problem at the beginning of this blog post? Well, as it happens, we can use <code>local-apply-transformer</code> to implement a macro that allows expansion <em>anywhere</em> using some simple tricks. While it’s true that we cannot magically divine which locations ought to be expanded, what we <em>can</em> do is explicitly annotate which places to expand.</p><p>To do this, we will implement a macro, <code>expand-inside</code>, that looks for subforms annotated with a special <code>$expand</code> identifier and performs macro transformation on those locations before proceeding with ordinary macroexpansion. Using the <code>clj-binding-pairs</code> example from the beginning of this blog post, our solution to that problem will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Put another way, <code>expand-inside</code> will force eager expansion on any subform surrounded with an <code>$expand</code> annotation.</p><p>We’ll start by defining the <code>$expand</code> binding itself. This binding won’t mean anything at all outside of <code>expand-inside</code>, but we’d like it to be a unique binding so that users can rename it (using, <code>rename-in</code>, for example) if they wish. To do this, we’ll use the usual trick of defining it as a macro that always produces an error if it’s ever used:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"illegal outside an ‘expand-inside’ form"</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span></code></pre><p>Next, we’ll implement a syntax class that will form the bulk of our implementation of <code>expand-inside</code>. Since we need to find uses of <code>$expand</code> that might be deeply-nested inside the syntax object provided to <code>expand-inside</code>, we need to recursively look through the syntax object, find any instances of <code>$expand</code>, and put it all back together once we’re done. This can be done relatively cleanly using a recursive syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">do-expand-inside</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="n">$expand</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">$expand</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:do-expand-inside</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">a:do-expand-inside</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">b:do-expand-inside</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">reassembled</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">a.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">reassembled</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">reassembled</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>There are some tricky details to get right in the reassembly of pairs, since syntax lists are actually composed of ordinary pairs rather than syntax pairs, but ultimately, the code for walking a syntax object is small. The key case of this syntax class is the call to <code>do-$expand</code> in the first clause, which we have not yet defined. This function will actually handle performing the expansion by invoking <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">trans</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}})</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">static</span><span class="w"> </span><span class="p">(</span><span class="nb">disjoin</span><span class="w"> </span><span class="nb">procedure?</span><span class="w"> </span><span class="nb">set!-transformer?</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"syntax transformer"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">trans.value</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)])))</span></code></pre><p>This uses the handy <code>static</code> syntax class that comes with <code>syntax/parse</code>, which implicitly handles the call to <code>syntax-local-value</code> and produces a nice error message if the value returned does not match a predicate. All we have to do is apply the transformer value bound to the <code>trans.value</code> attribute using <code>local-apply-transformer</code>, and now the <code>expand-macro</code> can be written in just a couple lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">expand-inside</span> +<span class="w"> </span><span class="kd">#:track-literals</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:do-expand-inside</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form.expansion</span><span class="p">])</span></code></pre><p>(Using the <code>#:track-literals</code> option, also new in Racket v7.0, ensures that Check Syntax will be able to recognize the uses of <code>$expand</code> that disappear from after <code>expand-inside</code> is expanded.)</p><p>Putting everything together, our example from above really works:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>That’s it. All told, the entire implementation is only about 30 lines of code. For a full, compilable, working example, see <a href="https://gist.github.com/lexi-lambda/65d69043023b519694f50dfca2dc7d33">this gist</a>.</p><ol class="footnotes"></ol></article>Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions2018-09-13T00:00:00Z2018-09-13T00:00:00ZAlexis King<article><p>In my <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">previous blog post</a>, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.</p><h2><a name="what-are-internal-definitions"></a>What are internal definitions?</h2><p>This blog post is going to be largely focused on how to properly implement a form that handles the expansion of <em>internal definitions</em> in Racket. This is a tricky topic to get right, but before we can discuss internal definitions, we have to establish what definitions themselves are and how they relate to other binding forms.</p><p>In a traditional Lisp, there are two kinds of bindings: top-level bindings and local bindings. In Scheme and its descendants, this distinction is characterized by two different binding forms, <code>define</code> and <code>let</code>. To a first approximation, <code>define</code> is used for defining top-level, global bindings, and it resembles variable definitions in many mainstream languages in the sense that definitions using <code>define</code> are not really expressions. They don’t produce a value, they define a new binding. Definitions written with <code>define</code> look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="s2">"hello"</span><span class="p">)</span></code></pre><p>Each definition is made up of two parts: the <em>binding identifier</em>, in this case <code>x</code> and <code>y</code>, and the <em>right hand side</em>, or RHS for short. Each RHS is a single expression that will be evaluated and used as the value for the introduced binding.</p><p>In Scheme and Racket, <code>define</code> also supports a shorthand form for defining functions in a natural syntax without the explicit need to write <code>lambda</code>, which looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">double</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span></code></pre><p>However, this is just syntactic sugar. The above form is really just a macro for the following equivalent, expanded version:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">double</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)))</span></code></pre><p>Since we only care about fully-expanded programs, we’ll focus exclusively on the expanded version of <code>define</code> in this blog post, since if we handle that, we’ll also handle the function shorthand’s expansion.</p><p>In contrast to <code>define</code>, there is also <code>let</code>, which has a rather different shape. A <code>let</code> form <em>is</em> an expression, and it creates local bindings in a delimited scope:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>The binding clauses of a <code>let</code> expression are known as the <em>binding pairs</em>, and the sequence of expressions afterwards are known as the <em>body</em> of the <code>let</code>. Each binding pair consists of a binding identifier and a RHS, just like a top-level definition created with <code>define</code>, but while <code>define</code> is a standalone form, the binding pairs cannot meaningfully exist outside of a <code>let</code>—they are recognized as part of the grammar of the <code>let</code> form itself.</p><p>Like other Lisps, Racket distinguishes between top-level—or, more precisely, <em>module-level</em>—bindings and local bindings. A module-level binding can be exported using <code>provide</code>, which will allow other modules to access the binding by importing the module with <code>require</code>. Such definitions are treated specially by the macroexpander, compiler, and runtime system alike. There is a pervasive, meaningful difference between module-level definitions and local definitions besides simply scope.</p><p>I am making an effort to make this as clear as possible before discussing internal definitions because without it, the following point can be rather confusing: internal definitions are written using <code>define</code>, but they are local bindings, <em>not</em> module-level ones! In Racket, <code>define</code> is allowed to appear in the body of virtually all block forms like <code>let</code>, so the following is a legal program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>This program is equivalent to the one expressed using <code>let</code>. In fact, when the Racket macroexpander expands these local uses of <code>define</code>, it actually translates them into uses of <code>letrec</code>. After expanding the above expression, it would look closer to the following:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span></code></pre><p>In this sense, <code>define</code> is a form with a double life in Racket. When used at the module level, it creates module-level definitions, which remain in a fully-expanded program and can be imported by other modules. When used inside local blocks, it creates internal definitions, which do not remain in fully expanded programs, since they are translated into recursive local binding forms.</p><p>In this blog post, we will ignore module-level definitions. Like in the previous blog post, we will focus exclusively on expanding expressions, not whole modules. However, we will extend our language to allow internal definitions inside local binding forms, and we will translate them into <code>letrec</code> forms in the same way as the Racket macroexpander.</p><h2><a name="revisiting-and-generalizing-the-expression-expander"></a>Revisiting and generalizing the expression expander</h2><p>In the previous blog post, our expander expanded types, which were essentially expressions from the perspective of the Racket macroexpander. We wrote a syntax class that handled the parsing of a restricted type grammar that disallowed most Racket-level expression forms, like <code>begin</code>, <code>if</code>, <code>#%plain-lambda</code>, and <code>quote</code>. After all, Hackett is not dependently-typed, and it disallows explicit type abstraction to preserve type inference, so it would be a very bad thing if we allowed <code>if</code> or explicit lambda abstraction to appear in our types. For this blog post, however, we will restructure the type expander to handle the full grammar of expressions permitted by Racket.</p><p>While the syntax class approach used in the previous blog post was cute, this blog post will use ordinary functions defined at phase 1 instead of syntax classes. In practice, this provides superior error reporting, since it reports syntax errors in terms of the form that went wrong, not the form prior to expansion. Since we can still use <code>syntax-parse</code> to parse the arguments to these functions, we don’t lose any expressive power in the expression of our pattern language.</p><p>To start, we’ll extract the call to <code>local-expand</code> into its own function. This corresponds to the <code>type</code> syntax class from the previous blog post, but we’ll use phase 1 parameters to avoid threading so many explicit function arguments around:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-context</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-stop-list</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">))))</span></code></pre><p>Due to the way <code>local-expand</code> implicitly extends the stop list, as discussed in the previous blog post, we can initialize the stop list to a list containing just <code>define-values</code> and <code>define-syntaxes</code>, and the other forms we care about will be included automatically.</p><p>Next, we’ll use this function to implement a <code>expand-expression</code> function, which will emulate the way the expander expands a single expression, as the name implies. We’ll ignore any custom core forms for now, so we’ll just focus exclusively on the Racket core forms.</p><p>A few of Racket’s core forms are not actually subject to any expansion at all, and they expand to themselves. These forms are <code>quote</code>, <code>quote-syntax</code>, and <code>#%variable-reference</code>. Additionally, <code>#%top</code> is not something useful to handle ourselves, since it involves no recursive expansion, so we’ll treat it as if it expands to itself as well and allow the expander to raise any unbound identifier errors it produces. Here’s what the <code>expand-expression</code> function looks like when exclusively handling these things:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Another set of Racket core forms are simple expressions which contain subforms, all of which are themselves expressions. These forms include things like <code>#%expression</code>, <code>begin</code>, and <code>if</code>, and they can be expanded recursively. We’ll add another clause to handle these, which can be written with a straightforward recursive call to <code>expand-expression</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Another easy form to handle is <code>set!</code>, since it also requires simple recursive expansion, but it can’t be handled in the same way as the above forms since one of its subforms (the variable to mutate) should not be expanded. It needs another small clause:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:set!</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)))]</span></code></pre><p>The other expressions are harder, since they’re all the binding forms. Fully-expanded Racket code has four local binding forms: <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, and <code>letrec-values</code>. Additionally, as discussed in the previous blog post, <code>local-expand</code> can also produce <code>letrec-syntaxes+values</code> forms produced by local syntax bindings. In the type expander, we completely disallowed runtime bindings from appearing in the resulting program, so we completely removed <code>letrec-syntaxes+values</code> in our expansion, but in the case of handling arbitrary Racket programs, we actually want to leave a <code>letrec-values</code> form behind to hold any runtime bindings (i.e. the <code>values</code> part of <code>letrec-syntaxes+values</code>).</p><p>We’ll start with <code>#%plain-lambda</code>, which is the simplest of all the five aforementioned binding forms. It binds a sequence of identifiers at runtime, and they are in scope within the body of the lambda expression. Just as we created and used an internal-definition context to hold the bindings of a <code>letrec-syntax+values</code> form in the previous blog post, we’ll do the same for Racket’s other binding forms as well:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>However, the above handling of <code>#%plain-lambda</code> isn’t <em>quite</em> right, since the argument list can also include a “rest argument” binding in addition to a sequence of positional arguments. To accommodate this, we can introduce a simple syntax class that handles the different permutations:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">plain-formals</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"formals"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[[</span><span class="n">id</span><span class="w"> </span><span class="mi">1</span><span class="p">]]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id*:id</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id**:id</span><span class="p">)</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">id*</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">id**</span><span class="p">]]))</span></code></pre><p>Now we can use this to adjust <code>#%plain-lambda</code> to handle rest arguments:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Next, we’ll handle <code>case-lambda</code>. As it turns out, expanding <code>case-lambda</code> is almost exactly the same as expanding <code>#%plain-lambda</code>, except that it has multiple clauses. Since each clause is expanded identically to the body of a <code>#%plain-lambda</code>, and it even has the same shape, the clauses can be extracted into a separate syntax class to share code between the two forms:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]]))</span></code></pre><p>Now, both <code>#%plain-lambda</code> and <code>case-lambda</code> can be handled in a few lines of code each:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Finally, we need to tackle the three <code>let</code> forms. None of these involve any fundamentally new ideas, but they are a little bit more involved than the variants of lambda due to the need to handle the RHSs. Each variant is slightly different, but not dramatically so: the bindings aren’t in scope when expanding the RHSs of <code>let-values</code>, but they are for <code>letrec-values</code> and <code>letrec-syntaxes+values</code>, and <code>letrec-syntaxes+values</code> creates transformer bindings and must evaluate some RHSs in phase 1 while <code>let-values</code> and <code>letrec-values</code> exclusively bind runtime bindings. It would be possible to implement these three forms in separate clauses, but since we’d ideally like to duplicate as little code as possible, we can write a rather elaborate <code>syntax/parse</code> pattern to handle all three binding forms all at once.</p><p>We’ll start by handling <code>let-values</code> alone to keep things simple:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>This isn’t dramatically different from the implementation of <code>#%plain-lambda</code>. The only difference is that we have to recursively invoke <code>expand-expression</code> on the RHSs in addition to expanding the body expressions. To handle <code>letrec-values</code> in the same clause, however, we’ll have to get a little more creative.</p><p>So far, we haven’t actually tapped very far into <code>syntax/parse</code>’s pattern language over the course of these two blog posts. The full language available to patterns is rather extensive, and we can take advantage of that to write a modification of the above clause that handles both <code>let-values</code> and <code>letrec-values</code> at once:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}}}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>The <code>~bind</code> pattern allows us to explicitly control how attributes are bound as part of the pattern-matching process, which allows us to track when we want to enable the recursive binding behavior of <code>letrec-values</code> in our handler code. Since the vast majority of the logic is otherwise identical, this is a significant improvement over duplicating the clause.</p><p>Adding support for <code>letrec-syntaxes+values</code> is done in the same general way, but the pattern is even more involved. In addition to tracking whether or not the bindings are recursive, we have to track if any syntax bindings were present at all, and if they were, bind them with <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span></code></pre><p>This behemoth clause handles all three varieties of <code>let</code> forms that can appear in the result of <code>local-expand</code>. Notably, in the <code>letrec-syntaxes+values</code> case, we expand into <code>letrec-values</code>, since the transformer bindings are effectively erased, and we use <code>syntax-track-origin</code> to record that the result originally came from a use of <code>letrec-syntaxes+values</code>.</p><p>With these five clauses, we’ve handled all the special forms that can appear in expression position in Racket’s kernel language. To tie things off, we just need to handle the cases of a variable reference, which is represented by a bare identifier not bound to syntax, or literal data, like numbers or strings. We can add one more clause at the end to handle those:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span></code></pre><p>Putting them all together, our <code>expand-expression</code> function looks as follows:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> + +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">])))</span></code></pre><p>If we try it out, we’ll see that it really does work! Even complicated local binding forms are handled properly by our expander:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">))))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">(((</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>We are now able to expand arbitrary Racket expressions in the same way that the expander does. While this might not seem immediately useful—after all, we haven’t actually gained anything here over just calling <code>local-expand</code> with an empty stop list—we can use this as the basis of an expander that can extensibly handle custom core forms, which I may cover in a future blog post.</p><h2><a name="adding-support-for-internal-definitions"></a>Adding support for internal definitions</h2><p>In the previous section, we defined an expander that could expand arbitrary Racket expressions, but our expander is still imperfect: we still do not support internal definitions. For all forms that have bodies, including <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, and <code>letrec-syntaxes+values</code>, Racket permits the use of internal definitions.</p><p>In practice, internal-definition contexts allow for an increased degree of modularity compared to traditional local binding forms, since they provide an <em>extensible</em> binding language. Users may mix many different binding forms within a single definition context, such as <code>define</code>, <code>define-syntax</code>, <code>match-define</code>, and even <code>struct</code>. However, this means the rewriting process described earlier in this blog post is not as simple as detecting the definitions and lifting them into a local binding form, since it’s not immediately apparent which forms are binding forms and which are expressions!</p><p>For this reason, expanding internal-definition contexts happens to be a nontrivial problem in itself. It involves a little more care than expanding expressions does, since it requires using partial expansion to discover which forms are definitions and which forms are expressions. We must take care to never expand too much, but also to expand enough that we reveal all uses of <code>define-values</code> and <code>define-syntaxes</code> (which all definition forms eventually expand into). We also must handle the splicing behavior of <code>begin</code>, which is necessary to allow single forms to expand into multiple definitions.</p><p>We’ll start by writing an <code>expand-body</code> function, which operates similarly to our previous <code>expand-expression</code> function. Unlike <code>expand-expression</code>, <code>expand-body</code> will accept a <em>list</em> of syntax objects, which represents the sequence of forms that make up the body. Logically, each body will create a first-class definition context with <code>syntax-local-make-definition-context</code> to represent the sequence of definitions:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>The bulk of our <code>expand-body</code> function will be a loop that partially expands body forms, adds definitions to the definition context as it discovers them, and returns the expressions and runtime definitions to be rewritten into binding pairs for a <code>letrec-values</code> form. Additionally, the loop will also track so-called <em>disappeared uses</em> and <em>disappeared bindings</em>, which are attached to the expansion using syntax properties to allow tools like DrRacket to learn about the binding structure of phase 1 definitions that are erased as part of macroexpansion.</p><p>The skeleton of this loop is relatively straightforward to write. We will iterate over the syntax objects that make up the body, expand them, and process the expansion using <code>syntax-parse</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">)))))))</span></code></pre><p>The hard part, of course, is actually handling the potential results of that expansion. We need to handle three forms specially: <code>begin</code>, <code>define-values</code>, and <code>define-syntaxes</code>. All other results of partial expansion will be treated as expressions. We’ll start by handling <code>begin</code>, since it’s the simplest case; we only need to prepend the subforms to the list of body forms to be processed, then continue looping:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>However, as is often the case, this isn’t quite perfect, since the information that these forms came from a surrounding <code>begin</code> is lost, which tools like DrRacket want to know. To solve this problem, the expander adjusts the <code>origin</code> property for all spliced forms, which we can mimic using <code>syntax-track-origin</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This is sufficient for <code>begin</code>, so we can move onto the actual definitions themselves. This actually isn’t too hard, since we just need to add the bindings we discover to the first-class definition context and preserve <code>define-values</code> bindings as binding pairs:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This solution is missing one thing, however, which is the use of <code>syntax-local-identifier-as-binding</code> to any use-site scopes that were added to the binding identifier while expanding the binding form in the definition context. Explaining precisely why this is necessary is outside the scope of this blog post, and is best understood by reading <a href="http://www.cs.utah.edu/plt/scope-sets/pattern-macros.html#%28part._use-site%29">the section on use-site scopes</a> in the paper that describes the theory behind Racket’s current macro system, Bindings as Sets of Scopes. In any case, the impact on our implementation is small:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>Finally, as with <code>begin</code>, we want to track that the binding pairs we generate actually came from a use of <code>define-values</code> (which in turn likely came from a use of some other definition form). Therefore, we’ll add another use of <code>syntax-track-origin</code> to copy and extend the necessary properties:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>That’s it for <code>define-values</code>. All that’s left is to handle <code>define-syntaxes</code>, which is quite similar, but instead of storing the definition in a binding pair, its RHS is immediately evaluated and added to the definition context using <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span></code></pre><p>As the above snippet indicates, this is also where the disappeared uses and disappeared bindings come in. In previous cases, we’ve used <code>syntax-track-origin</code> to indicate that a piece of syntax was the result of expanding a different piece of syntax, but in this case, <code>define-syntaxes</code> doesn’t expand into anything at all; it’s simply removed from the expansion entirely. Therefore, we need to resort to tracking the information in syntax properties on the resulting <code>letrec-values</code> form, so we’ll save them for later.</p><p>Finally, to finish things up, we can add a catchall clause that handles all other forms, which are now guaranteed to be expressions:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This completes our loop that processes definition forms, so all that’s left to do is handle the results. The only significant remaining work is to actually expand the RHSs of the binding pairs we collected and the body expressions, which can be done by calling our own <code>expand-expression</code> function directly:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span></code></pre><p>Finally, we can assemble all the pieces together into a single local binding form with the appropriate syntax properties:</p><pre><code class="pygments"><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))</span></code></pre><p>That’s it. We’ve now written an <code>expand-body</code> function that can process internal definition contexts in the same way that the macroexpander does. Overall, the whole function is just under 45 lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)))))</span></code></pre><p>The next step is to actually use this function. We need to replace certain recursive calls to <code>expand-expression</code> with calls to <code>expand-body</code>, but if we do this naïvely, we’ll have some problems. Currently, when we expand body forms, they’re always immediately inside another definition context (i.e. the bindings introduced by lambda formals or by <code>let</code> binding pairs), but they haven’t actually been expanded in that context yet. When we call <code>expand-body</code>, we create a nested context, which will inherit the bindings, but won’t automatically add the parent context’s scope. Therefore, we need to manually call <code>internal-definition-context-introduce</code> on the body syntax objects before calling <code>expand-body</code>. We can write a small helper function to make this easier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="n">stxs</span><span class="w"> </span><span class="n">ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">ctx</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stxs</span><span class="p">))))))</span></code></pre><p>Now we just need to replace the relevant calls to <code>expand-expression</code> with calls to <code>expand-body/in-ctx</code>, starting with a minor adjustment to our <code>lambda-clause</code> syntax class from earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="p">]]))</span></code></pre><p>The only other change must occur in the handling of the various <code>let</code> forms, which similarly replaces <code>expand-expression</code> with <code>expand-body/in-ctx</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">)))]</span></code></pre><p>With these changes, we’ve now extended our expression expander with the ability to expand internal definitions. We can see this in action on a simple example:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>Just as we’d like, the transformer bindings were expanded and subsequently eliminated, and the runtime binding was collected into a <code>letrec-values</code> form. The outer <code>let-values</code> is left over from the outer <code>let</code>, which is needed only to create an internal-definition context to hold our internal definitions.</p><h2><a name="putting-the-expression-expander-to-work"></a>Putting the expression expander to work</h2><p>So far, we’ve done a lot of work to emulate the behavior of Racket’s macroexpander, and as the above example demonstrates, we’ve been fairly successful in that goal. However, you might be wondering <em>why</em> we did any of this, as replicating the behavior of <code>local-expand</code> is not very useful on its own. As mentioned above, this can be used as the foundation of an expander for custom core forms that extends, rather than replaces, the built-in Racket core forms, It can also be used to “cheat” and expand through the behavior of the <code>local-expand</code> stop list, which implicitly adds the Racket core forms to any non-empty stop list. Hopefully, I’ll have a chance to cover some of these things more deeply in the future, but for now, I’ll just give a small taste of the latter.</p><p>By using the power of our <code>expand-expression</code> function, it’s actually possible to use this kind of expression expander to do genuinely nefarious things, such as hijack the behavior of arbitrary macros! For example, we could do something evil like make <code>for</code> loops run in reverse order by adding <code>for</code> to <code>current-stop-list</code>, then adding an additional special case to <code>expand-expression</code> for <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">for</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="k">for</span><span class="p">]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:for</span><span class="w"> </span><span class="p">([</span><span class="n">x:id</span><span class="w"> </span><span class="n">seq:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="p">(</span><span class="nb">sequence-&gt;list</span><span class="w"> </span><span class="n">seq</span><span class="p">)))]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>Amazingly, due to the fact that we’ve taken complete control of the expansion process, this will rewrite uses of <code>for</code> <em>even if they are introduced by macroexpansion</em>. For example, we could write a small macro that expands into a use of <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="nb">in-range</span><span class="w"> </span><span class="n">n</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">i</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span> +<span class="mi">0</span> +<span class="mi">1</span> +<span class="mi">2</span> +<span class="mi">3</span> +<span class="mi">4</span></code></pre><p>If we write a wrapper macro that applies our evil version of <code>expand-expression</code> to its body, then wrap a use of our <code>print-up-to</code> macro with it, it will execute the loop in reverse order:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:expr</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span><span class="p">)])</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>On its own, this is not that impressive, since we could have just used <code>local-expand</code> on the body directly to achieve this. However, what’s remarkable about <code>hijack-for-loops</code> is that it will work even if the <code>for</code> loop is buried deep inside some arbitrary expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">foo</span> +<span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">))))</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="mi">5</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>Of course, this example is rather contrived—mucking with <code>for</code> loops like this isn’t useful at all, and nobody would really write <code>print-up-to</code> as a macro, anyway—but there is potential for using this technique to do more interesting things.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p>The system outlined in this blog post is not something I would recommend using in any real macro. It is enormously complicated, requires knowledge well above that of your average working macrologist, and it involves doing rather horrible things to the macro system, things it was undoubtably never designed to do. Still, I believe this blog post is useful, for a few different reasons:</p><ol><li><p>The technology outlined in this post, while perhaps not directly applicable to existing real-world problems, provides a framework for implementing various new kinds of syntax transformations in Racket <em>without</em> extending the macro system. It demonstrates the expressive power of the macro system, and it hopefully lays the foundation for a better, more high-level interface for users who wish to define their own languages with custom core forms.</p></li><li><p>This system provides insight into the way the Racket macroexpander operates, <em>in terms of the userspace syntax API</em>. The canonical existing model of hygienic macroexpansion, in the aforementioned <a href="http://www.cs.utah.edu/plt/scope-sets/">Bindings as Sets of Scopes</a> paper, does not explain the workings of internal definition contexts in detail, and it certainly doesn’t explain them in terms that a Racket programmer would already be familiar with. By reencoding those ideas within the macro system itself, an advanced macro writer may be able to more easily connect concepts in the macro system’s implementation to concepts they have already been exposed to.</p></li><li><p>The capability of the proof-of-concept outlined here demonstrates that the limitation imposed by the existing implementation of the stop list (namely, the way it is implicitly extended with additional identifiers) is essentially artificial, and it can be hacked around with sufficient (albeit significant) effort. This isn’t enormously important, but it is somewhat relevant to a recent debate in <a href="https://github.com/racket/racket/issues/2154">a GitHub issue</a> about the handling of the <code>local-expand</code> stop list.</p></li><li><p>Finally, for myself as much as anyone else, this implementation records in a concise way (perhaps overly concise at times) the collection of very subtle details I’ve learned over the past six months about how information is preserved and propagated during the expansion process.</p></li></ol><p>This blog post is not for everybody. If you made it to the end, give yourself a pat on the back. If you made it to the end <em>and</em> understood everything you read: congratulations, you are a certified expert in Racket macro programming. If not, do not fear, and do not lose hope—I plan for something significantly more mellow next time.</p><p>As always, I’d like to give thanks to the people who contributed significantly, if indirectly, to the contents of this blog post, namely <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://mballantyne.net">Michael Ballantyne</a>, and <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a>. And finally, for those interested, all of the code in this blog post can be found in a runnable form <a href="https://gist.github.com/lexi-lambda/c4f4b91ac9c0a555447d72d02e18be7b">in this GitHub gist</a>.</p><ol class="footnotes"></ol></article>Reimplementing Hackett’s type language: expanding to custom core forms in Racket2018-04-15T00:00:00Z2018-04-15T00:00:00ZAlexis King<article><p>In the past couple of weeks, I <a href="https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935">completely rewrote the implementation of Hackett’s type language</a> to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.</p><p>This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">my previous blog post on Hackett</a>, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.</p><h2><a name="what-are-core-forms"></a>What are core forms?</h2><p>Before we can get started writing <em>custom core forms</em>, we need to understand the meaning of Racket’s plain old <em>core forms</em>. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.</p><p>To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a <code>#lang</code> line at the top. In this case, we’ll use <code>#lang racket</code> to keep things simple:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as <em>reading</em> the program. The <code>#lang</code> line controls how the program is read—some <code>#lang</code>s provide parsers that allow syntax that is very different from the parser used for <code>#lang racket</code>—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span></code></pre><p>Note the introduction of <code>#%module-begin</code>. Despite the fancy name, this is really just an ordinary macro provided by the <code>racket</code> language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with <code>#%module-begin</code>; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.</p><p>One the program has been read, it is subsequently <em>expanded</em> by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">2</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">call-with-values</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="n">add2</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">))</span> +<span class="w"> </span><span class="n">print-values</span><span class="p">)))</span></code></pre><p>Let’s note the things that changed:</p><ol><li><p><code>#%module-begin</code> was replaced with <code>#%plain-module-begin</code>. <code>#%plain-module-begin</code> is a binding that wraps the body of every expanded module, and all definitions of <code>#%module-begin</code> in any language must eventually expand to <code>#%plain-module-begin</code>. However, <code>#lang racket</code>’s <code>#%module-begin</code> doesn’t <em>just</em> expand to <code>#%plain-module-begin</code>, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints <code>5</code> even though there is no code related to printing in the original program!</p></li><li><p>The lambda shorthand used with <code>define</code> was converted to an explicit use of <code>lambda</code>, and it was expanded to <code>define-values</code>. In Racket, <code>define</code> and <code>define-syntax</code> are really just macros for <code>define-values</code> and <code>define-syntaxes</code> that only bind a single identifier.</p></li><li><p>All function applications were tagged explicitly with <code>#%plain-app</code>. This syntactically distinguishes function applications from uses of forms like <code>define-values</code> or <code>lambda</code>. It also allows languages to customize function application by providing their own macros named <code>#%app</code> (just like languages can provide their own macros named <code>#%module-begin</code> that expand to <code>#%plain-module-begin</code>), but that is outside the scope of this blog post.</p></li><li><p>All literals have been wrapped with <code>quote</code>, so <code>2</code> became <code>'2</code> and <code>3</code> became <code>'3</code>.</p></li></ol><p>Importantly, the resulting program contains <strong>no macros</strong>. Such programs are called <em>fully expanded</em>, since all macros have been eliminated and no further expansion can take place.</p><p>So what’s left behind? Well, some of the things in the program are literal data, like the numbers <code>2</code> and <code>3</code>. There are also some variable references, <code>x</code> and <code>add2</code>. Most of the program, however, is built out of primitives like <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, and <code>lambda</code>. These primitives are <em>core forms</em>—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.</p><p>In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:</p><pre><code class="pygments"><span class="n">var</span> <span class="n">add2</span> <span class="o">=</span> + <span class="n">function</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span> + +<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">add2</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span></code></pre><p>Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be <em>compiled</em>. The Racket compiler has built-in rules for how to compile core forms like <code>define-values</code>, <code>lambda</code>, and <code>#%plain-app</code>, and the result is optimized Racket bytecode.</p><p>In the remainder of this blog post, as most discussions of macros do, we’ll ignore the <em>read</em> and <em>compile</em> steps of the Racket program pipeline and focus exclusively on the <em>expand</em> step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.</p><h3><a name="racket-s-default-core-forms"></a>Racket’s default core forms</h3><p>So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, <code>lambda</code>, and <code>quote</code>, but there are many more. The full list is available in the section of the Racket reference named <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29">Fully Expanded Programs</a>, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like <code>define-syntaxes</code>, <code>if</code>, <code>let-values</code>, <code>letrec-values</code>, <code>begin</code>, <code>quote-syntax</code>, and <code>set!</code>. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.</p><p>These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, <code>#lang racket</code> implements <code>cond</code> as a macro that eventually expands into <code>if</code>, and it implements <code>syntax</code> as a macro that eventually expands into function calls and <code>quote-syntax</code>. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s <code>match</code> can expand into uses of <code>let</code> and <code>cond</code>, and it doesn’t need to concern itself with using <code>let-values</code> and <code>if</code>. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.</p><h3><a name="the-need-for-custom-core-forms"></a>The need for custom core forms</h3><p>With that in mind, why might we wish to define <em>custom</em> core forms? In fact, what would such a thing even mean? By their very nature, <em>all</em> Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.</p><p>Despite this, there <em>are</em> at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.</p><h4><a name="supporting-multiple-backends"></a>Supporting multiple backends</h4><p>The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket <code>#lang</code> that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might <em>also</em> wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.</p><p>In the case of a hardware description language, the custom core forms might include things like <code>input</code> and <code>output</code> for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could <em>additionally</em> define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.</p><p>Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.</p><h4><a name="compiling-an-extensible-embedded-language"></a>Compiling an extensible embedded language</h4><p>A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.</p><p>This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it <em>will</em> eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.</p><p>Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the <code>match</code> macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.</p><p>Existing DSLs of this type are rare, but they do exist. <code>syntax/parse</code> provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from <code>racket/match</code> to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While <code>match</code> is essentially just a traditional pattern-matcher, albeit an extensible one, <code>syntax-parse</code> is its own programming language, closer in some ways to Prolog than to Racket.</p><p>For this reason, <code>syntax/parse</code> has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as <a href="http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29"><em>pattern directives</em></a>. Here is an example of pattern directives in action, from my own <code>threading</code> library:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>Each directive is represented by a keyword, in this case <code>#:do</code> and <code>#:with</code>. Each directive has a corresponding keyword in the pattern language, in this case <code>~do</code> and <code>~parse</code>. Therefore, the above pattern could equivalently be written this way:</p><pre><code class="pygments"><span class="p">[{</span><span class="n">~and</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">~do</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using <code>#:declare</code>, so this is also equivalent:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">expr</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a <code>#:with</code> or <code>~parse</code> pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using <code>#:do</code> or <code>~do</code>, practical uses of <code>syntax/parse</code> really do involve quite a lot of programming in its pattern DSL.</p><p>But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—<code>#:declare</code>, <code>#:do</code>, and <code>#:with</code>, among others—are essentially the core forms of <code>syntax/parse</code>’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.</p><p>But what if <code>syntax/parse</code> could define its own core forms? What if, instead of <code>#:do</code>, <code>#:declare</code>, and <code>#:with</code> being implemented as keyword options specially recognized by the <code>syntax-parse</code> grammar, it defined <code>do</code>, <code>declare</code>, and <code>with</code> as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the <code>syntax/parse</code> core forms. The implementation of <code>syntax/parse</code> could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.</p><p>Now, to be fair, <code>syntax/parse</code> is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.</p><p>The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.</p><h2><a name="the-need-for-a-custom-type-language-in-hackett"></a>The need for a custom type language in Hackett</h2><p>In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for <em>both</em> of them:</p><ul><li><p>Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.</p></li><li><p>Hackett can <em>also</em> make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.</p></li></ul><p>The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:</p><ul><li><p><code>(#%type:con <em>id</em>)</code> — Type constructors, like <code>Integer</code> or <code>Maybe</code>. These are one of the fundamental building blocks of Hackett types.</p></li><li><p><code>(#%type:app <em>type</em> <em>type</em>)</code> — Type application, such as <code>(Maybe Integer)</code>. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of <code>#%type:app</code>.</p></li><li><p><code>(#%type:forall <em>id</em> <em>type</em>)</code> — Universal quantification. This is essentially a binding form, which binds any uses of <code>(#%type:bound-var <em>id</em>)</code> in <code><em>type</em></code>.</p></li><li><p><code>(#%type:qual <em>type</em> <em>type</em>)</code> — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like <code>Eq</code> are bound as type constructors.</p></li><li><p>Finally, Hackett types support three different varieties of type variables:</p><ul><li><p><code>(#%type:bound-var <em>id</em>)</code> — Bound type variables. These are only legal under a corresponding <code>#%type:forall</code>.</p></li><li><p><code>(#%type:wobbly-var <em>id</em>)</code> — Solver variables, which may unify with any other type as part of the typechecking process.</p></li><li><p><code>(#%type:rigid-var <em>id</em>)</code> — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.</p></li></ul></li></ul><p>To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we <em>can</em> bind them to macros that do nothing but raise an exception if something attempts to expand them:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span></code></pre><p>This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.</p><h3><a name="expanding-types-in-our-type-language"></a>Expanding types in our type language</h3><p>We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we <em>do</em> want to expand some of their subforms. For example, in the type <code>(#%type:app a b)</code>, we want to recursively expand <code>a</code> and <code>b</code>.</p><p>In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a>, and it has an option relevant to our needs: the stop list.</p><p>Often, <code>local-expand</code> is used to force the expander to completely, recursively expand a form. For example, by using <code>local-expand</code>, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="c1">; =&gt; (let-values ([(x) &#39;1]) (#%plain-app + x &#39;2))</span></code></pre><p>The third argument to <code>local-expand</code> is the <em>stop list</em>, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; =&gt; (#%type:forall x t)</span></code></pre><p>Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the <code>#%type:forall</code> identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding <code>t</code> since the expander has no way of knowing which pieces of <code>(#%type:forall x t)</code> it should expand! In this case, we want it to recur to expand <code>t</code>, since it should be a type, but not <code>x</code>, since <code>#%type:forall</code> essentially puts <code>x</code> in binding position.</p><p>Therefore, we have to get more clever. We need to call <code>local-expand</code> to produce a type, then we have to pattern-match on it and subsequently call <code>local-expand</code> <em>again</em> on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.</p><p>One good way to do this is to use <code>syntax/parse</code> syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using <code>local-expand</code> and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled <code>#%type:con</code> and <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:expanded-type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]))</span></code></pre><p>This blog post is definitely <em>not</em> a <code>syntax/parse</code> tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named <code>expansion</code>. This attribute contains the fully expanded version of the type currently being parsed. In the <code>#%type:con</code> case, <code>expansion</code> is just <code>this-syntax</code>, which holds the current piece of syntax being parsed. This makes sense, since uses of <code>#%type:con</code> just expand to themselves—expanding <code>(#%type:con Maybe)</code> should not perform any additional expansion on <code>Maybe</code>. This is one of Hackett’s atomic types.</p><p>In contrast, <code>#%type:app</code> <em>does</em> recursively expand its arguments. By annotating its two subforms with <code>:type</code>, the <code>type</code> syntax class will invoke <code>local-expand</code> on each subform, which will in turn use <code>expanded-type</code> to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once <code>a</code> and <code>b</code> have been expanded, <code>#%type:app</code> reassembles them into a new syntax object using <code>#'(#%type:app a.expansion b.expansion)</code>, which replaces their unexpanded versions with their new, expanded versions.</p><p>We can see this behavior by writing a small <code>expand-type</code> function that will expand its argument:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>Now we can use it to observe what happens when we try expanding a type using <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; #%type:app: expected type</span> +<span class="c1">; at: Maybe</span> +<span class="c1">; in: (#%type:app Maybe Integer)</span></code></pre><p>Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined <code>Maybe</code> or <code>Integer</code> anywhere. Let’s do so! We can define them as simple macros that expand into uses of <code>#%type:con</code>, which can be done easily using <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29"><code>make-variable-like-transformer</code></a> from <code>syntax/transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Maybe</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Integer</span><span class="p">)))</span></code></pre><p>Now, if we try expanding that same type again:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Integer))</span></code></pre><p>…it works! Neat. Now we just need to add the cases for the remaining forms in our type language:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">t:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like <code>let-syntax</code> to work in types. For example, we ought to be able to create a local type binding with <code>let-syntax</code> and have it just work. Unfortunately, it doesn’t:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; let-syntax: expected one of these identifiers: `#%type:con&#39;, `#%type:app&#39;, `#%type:forall&#39;, `#%type:qual&#39;, `#%type:bound-var&#39;, `#%type:wobbly-var&#39;, or `#%type:rigid-var&#39;</span> +<span class="c1">; at: letrec-syntaxes+values</span> +<span class="c1">; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))</span></code></pre><p>What went wrong? And why is it complaining about <code>letrec-syntaxes+values</code>? Well, if you read the documentation for <code>local-expand</code>, you’ll find that its behavior is a little more complicated than you might at first believe:</p><blockquote><p>If <em><code>stop-ids</code></em> is [a nonempty list containing more than just <code>module*</code>], then <code>begin</code>, <code>quote</code>, <code>set!</code>, <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, <code>if</code>, <code>begin0</code>, <code>with-continuation-mark</code>, <code>letrec-syntaxes+values</code>, <code>#%plain-app</code>, <code>#%expression</code>, <code>#%top</code>, and <code>#%variable-reference</code> are implicitly added to <em><code>stop-ids</code></em>. Expansion stops when the expander encounters any of the forms in <em><code>stop-ids</code></em>, and the result is the partially-expanded form.</p></blockquote><p>That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with <code>letrec-syntaxes+values</code> (which <code>let-syntax</code> expands to) is a reasonable one. If the expander naïvely expanded <code>letrec-syntaxes+values</code> in the presence of a nonempty stop list, it could cause some significant problems!</p><p>Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">))</span></code></pre><p>We see <code>let-syntax</code>, so we start by evaluating the expression on the right hand side of the <code>Bool</code> binding. This produces a transformer expression, so we bind <code>Bool</code> to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:</p><pre><code class="pygments"><span class="c1">; local bindings:</span> +<span class="c1">; Bool -&gt; #&lt;variable-like-transformer&gt;</span> +<span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)</span></code></pre><p>Now, the identifier in application position is <code>#%type:app</code>, and <code>#%type:app</code> is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the <code>let-syntax</code> needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is <code>(#%type:app Maybe Bool)</code>. But this is a problem, because when we then go to expand <code>Bool</code>, <code>Bool</code> isn’t in the local binding table anymore! The <code>let-syntax</code> was already erased, and <code>Bool</code> is unbound!</p><p>When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.</p><p>Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call <code>local-expand</code>. The bad news is that first-class definition contexts are <em>complicated</em>, and using them properly is a surprisingly subtle problem.</p><p>Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our <code>type</code> and <code>expanded-type</code> syntax classes so that we may thread a definition context around as we recursively expand:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now, we can add an additional case to <code>expanded-type</code> to handle <code>letrec-syntaxes+values</code>, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>But even this isn’t quite right. The problem with this implementation is that it throws away the existing <code>intdef-ctx</code> argument to <code>expanded-type</code>, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a <em>child</em> of the previous definition context by passing the old context as an argument to <code>syntax-local-make-definition-context</code>. This will ensure the parent bindings are brought into scope when expanding using the child context:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>With this in place, our example using <code>let-syntax</code> actually works!</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Bool))</span></code></pre><p>Pretty cool, isn’t it?</p><h3><a name="preserving-syntax-properties-and-source-locations"></a>Preserving syntax properties and source locations</h3><p>We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.</p><p>To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports <code>#%type:app</code> under a different name, like <code>#%type:apply</code>, we should expand to a piece of syntax that still has <code>#%type:apply</code> in application position instead of replacing it with <code>#%type:app</code>.</p><p>To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the <code>#%type:app</code> clause to the following:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span></code></pre><p>But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy <em>everything</em>, we can define two helper macros, <code>syntax/loc/props</code> and <code>quasisyntax/loc/props</code>, which are like <code>syntax/loc</code> and <code>quasisyntax/loc</code> but copy properties in addition to source location information:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span></code></pre><p>Using <code>syntax/loc/props</code>, we can be truly thorough about ensuring all properties are preserved:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span></code></pre><p>Applying this to the other relevant clauses, we get an updated version of the <code>expanded-type</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now we’re getting closer, but if you can believe it, even <em>this</em> isn’t good enough. The real expander’s implementation of <code>letrec-syntaxes+values</code> does two things our implementation does not: it copies properties and updates the <code>'origin</code> property to indicate the syntax came from a use of <code>letrec-syntaxes+values</code>, and it adds a <code>'disappeared-use</code> property to record the erased bindings for use by tools like DrRacket. We can apply <code>syntax-track-origin</code> and <code>internal-definition-context-track</code> to the resulting syntax to add the same properties the expander would:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span></code></pre><p>Now we’ve <em>finally</em> dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.</p><h3><a name="connecting-our-custom-language-to-hackett"></a>Connecting our custom language to Hackett</h3><p>It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-meta</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/parse</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/intdef</span> +<span class="w"> </span><span class="n">threading</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a <em>typechecker</em>. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.</p><p>But we don’t <em>just</em> need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom <code>#%app</code> binding that converts n-ary type applications to nested uses of <code>#%type:app</code>, as well as a nicer <code>forall</code> macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of <code>#%app</code>—one for types and one for values—since <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">Hackett supports multiple namespaces</a>, and each can have its own <code>#%app</code> binding.</p><p>The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom <code>syntax/parse</code> pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, <a href="https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt">it’s available on GitHub here</a>.</p><h2><a name="evaluation-limitations-and-acknowledgements"></a>Evaluation, limitations, and acknowledgements</h2><p>Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started <a href="https://twitter.com/lexi_lambda/status/976533916596097024">my new job</a> this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net <em>removal</em> of 100 lines from the rest of the codebase, which I consider an organizational win.</p><p>As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I <a href="https://twitter.com/lexi_lambda/status/985051504867446786">able to get it working</a> quickly and easily, I was able to do it in <a href="https://twitter.com/lexi_lambda/status/985052476473856000">no more than 20 lines of code</a>. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.</p><p>Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the <code>local-expand</code> stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes <code>syntax-parameterize</code>, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like <code>syntax-parameterize</code> cooperate better with things like Hackett’s type language.</p><p>Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> for <a href="https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf">his incredible work on constructing tools for the working macro developer</a>, including writing the fantastic <code>syntax/parse</code> library that powers essentially everything I do. Thank you both.</p><ol class="footnotes"></ol></article>An opinionated guide to Haskell in 20182018-02-10T00:00:00Z2018-02-10T00:00:00ZAlexis King<article><p>For me, this month marks the end of an era in my life: as of February 2018, I am no longer employed writing Haskell. It’s been a fascinating two years, and while I am excitedly looking forward to what I’ll be doing next, it’s likely I will continue to write Haskell in my spare time. I’ll probably even write it again professionally in the future.</p><p>In the meantime, in the interest of both sharing with others the small amount of wisdom I’ve gained and preserving it for my future self, I’ve decided to write a long, rather dry overview of a few select parts of the Haskell workflow I developed and the ecosystem I settled into. This guide is, as the title notes, <em>opinionated</em>—it is what I used in my day-to-day work, nothing more—and I don’t claim that anything here is the only way to write Haskell, nor even the best way. It is merely what I found helpful and productive. Take from it as much or as little as you’d like.</p><h2><a name="build-tools-and-how-to-use-them"></a>Build tools and how to use them</h2><p>When it comes to building Haskell, you have options. And frankly, most of them are pretty good. There was a time when <code>cabal-install</code> had a (warranted) reputation for being nearly impossible to use and regularly creating dependency hell, but I don’t think that’s the case anymore (though you <em>do</em> need to be a little careful about how you use it). Sandboxed builds work alright, and <code>cabal new-build</code> and the other <code>cabal new-*</code> commands are even better. That said, the UX of <code>cabal-install</code> is still less-than-stellar, and it has sharp edges, especially for someone coming from an ecosystem without a heavyweight compilation process like JavaScript, Ruby, or Python.</p><p>Nix is an alternative way to manage Haskell dependencies, and it seems pretty cool. It has a reputation for being large and complicated, and that reputation does not seem especially unfair, but you get lots of benefits if you’re willing to pay the cost. Unfortunately, I have never used it (though I’ve read a lot about it), so I can’t comment much on it here. Perhaps I’ll try to go all-in with Nix when I purchase my next computer, but for now, my workflow works well enough that I don’t feel compelled to switch.</p><p>Personally, I use <code>stack</code> as my Haskell build tool. It’s easy to use, it works out of the box, and while it doesn’t enjoy the same amount of caching as <code>cabal new-build</code> or Nix, it caches most packages, and it also makes things like git-hosted sources incredibly easy, which (as far as I can tell) can’t be done with <code>cabal-install</code> alone.</p><p>This section is going to be a guide on how <em>I</em> use <code>stack</code>. If you use <code>cabal-install</code> with or without Nix, great! Those tools seem good, too. This is not an endorsement of <code>stack</code> over the other build tools, just a description of how I use it, the issues I ran into, and my solutions to them.</p><h3><a name="understanding-stack-s-model-and-avoiding-its-biggest-gotcha"></a>Understanding <code>stack</code>’s model and avoiding its biggest gotcha</h3><p>Before using <code>stack</code>, there are a few things every programmer should know:</p><ul><li><p><code>stack</code> is not a package manager, it is a build tool. It does not manage a set of “installed” packages; it simply builds targets and their dependencies.</p></li><li><p>The command to build a target is <code>stack build &lt;target&gt;</code>. Just using <code>stack build</code> on its own will build the current project’s targets.</p></li><li><p><strong>You almost certainly do not want to use <code>stack install</code>.</strong></p></li></ul><p>This is the biggest point of confusion I see among new users of <code>stack</code>. After all, when you want to install a package with <code>npm</code>, you type <code>npm install &lt;package&gt;</code>. So a new Haskeller decides to install <code>lens</code>, types <code>stack install lens</code>, and then later tries <code>stack uninstall lens</code>, only to discover that no such command exists. What happened?</p><p><code>stack install</code> is not like <code>npm install</code>. <code>stack install</code> is like <code>make install</code>. It is nothing more than an alias for <code>stack build --copy-bins</code>, and <em>all</em> it does is build the target and copy all of its executables into some relatively global location like <code>~/.local/bin</code>. This is usually not what you want.</p><p>This design decision is not unique to <code>stack</code>; <code>cabal-install</code> suffers from it as well. One can argue that it isn’t unintuitive because it really is just following what <code>make install</code> conventionally does, and the fact that it happens to conflict with things like <code>npm install</code> or even <code>apt-get install</code> is just a naming clash. I think that argument is a poor one, however, and I think the decision to even include a <code>stack install</code> command was a bad idea.</p><p>So, remember: don’t use <code>stack install</code>! <code>stack</code> works best when everything lives inside the current project’s <em>local</em> sandbox, and <code>stack install</code> copies executables into a <em>global</em> location by design. While it might sometimes appear to work, it’s almost always wrong. The <em>only</em> situation in which <code>stack install</code> is the right answer is when you want to install an executable for a use unrelated to Haskell development (that is, something like <code>pandoc</code>) that just so happens to be provided by a Haskell package. <strong>This means no running <code>stack install ghc-mod</code> or <code>stack install intero</code> either, no matter what READMEs might tell you!</strong> Don’t worry: I’ll cover the proper way to install those things later.</p><h3><a name="actually-building-your-project-with-stack"></a>Actually building your project with <code>stack</code></h3><p>Okay, so now that you know to never use <code>stack install</code>, what <em>do</em> you use? Well, <code>stack build</code> is probably all you need. Let’s cover some variations of <code>stack build</code> that I use most frequently.</p><p>Once you have a <code>stack</code> project, you can build it by simply running <code>stack build</code> within the project directory. However, for local development, this is usually unnecessarily slow because it runs the GHC optimizer. For faster development build times, pass the <code>--fast</code> flag to disable optimizations:</p><pre><code>$ stack build --fast +</code></pre><p>By default, <code>stack</code> builds dependencies with coarse-grained, package-level parallelism, but you can enable more fine-grained, module-level parallel builds by adding <code>--ghc-options=-j</code>. Unfortunately, there are conflicting accounts on whether or not this actually makes things faster or slower in practice, and I haven’t extensively tested to see whether or not this is the case, so I mostly leave it off.</p><p>Usually, you also want to build and run the tests along with your code, which you can enable with the <code>--test</code> flag. Additionally, <code>stack test</code> is an alias for <code>stack build --test</code>, so these two commands are equivalent:</p><pre><code>$ stack build --fast --test +$ stack test --fast +</code></pre><p>Also, it is useful to build documentation as well as code! You can do this by passing the <code>--haddock</code> flag, but unfortunately, I find Haddock sometimes takes an unreasonably long time to run. Therefore, since I usually only care about running Haddock on my dependencies, I usually pass the <code>--haddock-deps</code> flag instead, which prevents having to re-run Haddock every time you build:</p><pre><code>$ stack test --fast --haddock-deps +</code></pre><p>Finally, I usually want to build and test my project in the background whenever my code changes. Fortunately, this can be done easily by using the <code>--file-watch</code> flag, making it easy to incrementally change project code and immediately see results:</p><pre><code>$ stack test --fast --haddock-deps --file-watch +</code></pre><p>This is the command I usually use to develop my Haskell projects.</p><h3><a name="accessing-local-documentation"></a>Accessing local documentation</h3><p>While Haskell does not always excel on the documentation front, a small amount of documentation is almost always better than no documentation at all, and I find my dependencies’ documentation to be an invaluable resource while developing. I find many people just look at docs on Hackage or use the hosted instance of Hoogle, but this sometimes leads people astray: they might end up looking at the wrong version of the documentation! Fortunately, there’s an easy solution to this problem, which is to browse the documentation <code>stack</code> installs locally, which is guaranteed to match the version you are using in your current project.</p><p>The easiest way to open local documentation for a particular package is to use the <code>stack haddock --open</code> command. For example, to open the documentation for <code>lens</code>, you could use the following command:</p><pre><code>$ stack haddock --open lens +</code></pre><p>This will open the local documentation in your web browser, and you can browse it at your leisure. If you have already built the documentation using the <code>--haddock-deps</code> option I recommended in the previous section, this command should complete almost instantly, but if you haven’t built the documentation yet, you’ll have to wait as <code>stack</code> builds it for you on-demand.</p><p>While this is a good start, it isn’t perfect. Ideally, I want to have <em>searchable</em> documentation, and fortunately, this is possible to do by running Hoogle locally. This is easy enough with modern versions of <code>stack</code>, which have built-in Hoogle integration, but it still requires a little bit of per-project setup, since you need to build the Hoogle search index with the following command:</p><pre><code>$ stack hoogle -- generate --local +</code></pre><p>This will install Hoogle into the current project if it isn’t already installed, and it will index your dependencies’ documentation and generate a new Hoogle database. Once you’ve done that, you can start a web server that serves a local Hoogle search page with the following command:</p><pre><code>$ stack hoogle -- server --local --port=8080 +</code></pre><p>Navigate to <code>http://localhost:8080</code> in your web browser, and you’ll have a fully-searchable index of all your Haskell packages’ documentation. Isn’t that neat?</p><p>Unfortunately, you <em>will</em> have to manually regenerate the Hoogle database when you install new packages and their documentation, which you can do by re-running <code>stack hoogle -- generate --local</code>. Fortunately, regenerating the database doesn’t take very long, as long as you’ve been properly rebuilding the documentation with <code>--haddock-deps</code>.</p><h3><a name="configuring-your-project"></a>Configuring your project</h3><p>Every project built with <code>stack</code> is configured with two separate files:</p><ul><li><p>The <code>stack.yaml</code> file, which controls which packages are built and what versions to pin your dependencies to.</p></li><li><p>The <code>&lt;project&gt;.cabal</code> file <em>or</em> <code>package.yaml</code> file, which specifies build targets, their dependencies, and which GHC options to apply, among other things.</p></li></ul><p>The <code>.cabal</code> file is, ultimately, what is used to build your project, but modern versions of <code>stack</code> generate projects that use hpack, which uses an alternate configuration file, the <code>package.yaml</code> file, to generate the <code>.cabal</code> file. This can get a little bit confusing, since it means you have <em>three</em> configuration files in your project, one of which is generated from the other one.</p><p>I happen to use and like hpack, so I use a <code>package.yaml</code> file and allow hpack to generate the <code>.cabal</code> file. I have no real love for YAML, and in fact I think custom configuration formats are completely fine, but the primary advantage of hpack is the ability to specify things like GHC options and default language extensions for all targets at once, instead of needing to duplicate them per-target.</p><p>You can think of the <code>.cabal</code> or <code>package.yaml</code> file as a specification for <em>how</em> your project is built and <em>what packages</em> it depends on, but the <code>stack.yaml</code> file is a specification of precisely <em>which version</em> of each package should be used and where it should be fetched from. Also, each <code>.cabal</code> file corresponds to precisely <em>one</em> Haskell package (though it may have any number of executable targets), but a <code>stack.yaml</code> file can specify multiple different packages to build, useful for multi-project builds that share a common library. The details here can be a little confusing, more than I am likely going to be able to explain in this blog post, but for the most part, you can get away with the defaults unless you’re doing something fancy.</p><h3><a name="setting-up-editor-integration"></a>Setting up editor integration</h3><p>Currently, I use Atom to write Haskell. Atom is not a perfect editor by any means, and it leaves a lot to be desired, but it’s easy to set up, and the Haskell editor integration is decent.</p><p>Atom’s editor integration is powered by <code>ghc-mod</code>, a program that uses the GHC API to provide tools to inspect Haskell programs. Installing <code>ghc-mod</code> must be done manually so that Atom’s <code>haskell-ghc-mod</code> package can find it, and this is where a lot of people get tripped up. They run <code>stack install ghc-mod</code>, it installs <code>ghc-mod</code> into <code>~/.local/bin</code>, they put that in their <code>PATH</code>, and things work! …except when a new version of GHC is released a few months later, everything stops working.</p><p>As mentioned above, <strong><code>stack install</code> is not what you want</strong>. Tools like <code>ghc-mod</code>, <code>hlint</code>, <code>hoogle</code>, <code>weeder</code>, and <code>intero</code> work best when installed as part of the sandbox, <em>not</em> globally, since that ensures they will match the current GHC version your project is using. This can be done per-project using the ordinary <code>stack build</code> command, so the easiest way to properly install <code>ghc-mod</code> into a <code>stack</code> project is with the following command:</p><pre><code>$ stack build ghc-mod +</code></pre><p>Unfortunately, this means you will need to run that command inside every single <code>stack</code> project individually in order to properly set it up so that <code>stack exec -- ghc-mod</code> will find the correct executable. One way to circumvent this is by using a recently-added <code>stack</code> flag designed for this explicit purpose, <code>--copy-compiler-tool</code>. This is like <code>--copy-bins</code>, but it copies the executables into a <em>compiler-specific location</em>, so a tool built for GHC 8.0.2 will be stored separately from the same tool built for GHC 8.2.2. <code>stack exec</code> arranges for the executables for the current compiler version to end up in the <code>PATH</code>, so you only need to build and install your tools once per compiler version.</p><p>Does this kind of suck? Yes, a little bit, but it sucks a whole lot less than all your editor integration breaking every time you switch to a project that uses a different version of GHC. I use the following command in a fresh sandbox when a Stackage LTS comes out for a new version of GHC:</p><pre><code>$ stack build --copy-compiler-tool ghc-mod hoogle weeder +</code></pre><p>This way, I only have to build those tools once, and I don’t worry about rebuilding them again until a the next release of GHC. To verify that things are working properly, you should be able to create a fresh <code>stack</code> project, run a command like this one, and get a similar result:</p><pre><code>$ stack exec -- which ghc-mod +/Users/alexis/.stack/compiler-tools/x86_64-osx/ghc-8.2.2/bin/ghc-mod +</code></pre><p>Note that this path is scoped to my operating system and my compiler version, but nothing else—no LTS or anything like that.</p><h2><a name="warning-flags-for-a-safe-build"></a>Warning flags for a safe build</h2><p>Haskell is a relatively strict language as programming languages go, but in my experience, it isn’t quite strict enough. Many things are not errors that probably ought to be, like orphan instances and inexhaustive pattern matches. Fortunately, GHC provides <em>warnings</em> that catch these problems statically, which fill in the gaps. I recommend using the following flags on all projects to ensure everything is caught:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat"><code>-Wcompat</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates"><code>-Wincomplete-record-updates</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints"><code>-Wredundant-constraints</code></a></p></li></ul><p>The <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> option turns on <em>most</em> warnings, but (ironically) not all of them. The <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Weverything"><code>-Weverything</code></a> flag truly turns on <em>all</em> warnings, but some of the warnings left disabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> really are quite silly, like warning when type signatures on polymorphic local bindings are omitted. Some of them, however, are legitimately useful, so I recommend turning them on explicitly.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat"><code>-Wcompat</code></a> enables warnings that make your code more robust in the face of future backwards-incompatible changes. These warnings are trivial to fix and serve as free future-proofing, so I see no reason not to turn these warnings on.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates"><code>-Wincomplete-record-updates</code></a> and <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a> are things I think ought to be enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> because they both catch what are essentially partial pattern-matches (and therefore runtime errors waiting to happen). The fact that <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a> <em>isn’t</em> enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> is so surprising that it can lead to bugs being overlooked, since the extremely similar <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-patterns"><code>-Wincomplete-patterns</code></a> <em>is</em> enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a>.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints"><code>-Wredundant-constraints</code></a> is a useful warning that helps to eliminate unnecessary typeclass constraints on functions, which can sometimes occur if a constraint was previously necessary but ends up becoming redundant due to a change in the function’s behavior.</p><p>I put all five of these flags in the <code>.cabal</code> file (or <code>package.yaml</code>), which enables them everywhere, but this alone is unlikely to enforce a warning-free codebase, since the build will still succeed even in the presence of warnings. Therefore, when building projects in CI, I pass the <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Werror"><code>-Werror</code></a> flag (using <code>--ghc-options=-Werror</code> for <code>stack</code>), which treats warnings as errors and halts the build if any warnings are found. This is useful, since it means warnings don’t halt the whole build while developing, making it possible to write some code that has warnings and still run the test suite, but it still enforces that pushed code be warning-free.</p><h2><a name="any-flavor-you-like"></a>Any flavor you like</h2><p>Haskell is both a language and a spectrum of languages. It is both a standard and a specific implementation. Haskell 98 and Haskell 2010 are good, small languages, and there are a few different implementations, but when people talk about “Haskell”, unqualified, they’re almost always talking about GHC.</p><p>GHC Haskell, in stark contrast to standard Haskell, is neither small nor particularly specific, since GHC ships with <em>dozens</em> of knobs and switches that can be used to configure the language. In theory, this is a little terrifying. How could anyone ever hope to talk about Haskell and agree upon how to write it if there are so many <em>different</em> Haskells, each a little bit distinct? Having a cohesive ecosystem would be completely hopeless.</p><p>Fortunately, in practice, this is not nearly as bad as it seems. The majority of GHC extensions are simple switches: a feature is either on or it is off. Turning a feature on rarely affects code that does not use it, so most extensions can be turned on by default, and programmers may simply avoid the features they do not wish to use, just as any programmer in any programming language likely picks a subset of their language’s features to use on a daily basis. Writing Haskell is not different in this regard, only in the sense that it does not allow all features to be used by default; everything from minor syntactic tweaks to entirely new facets of the type system are opt-in.</p><p>Frankly, I think the UX around this is terrible. I recognize the desire to implement a standard Haskell, and the old <code>-fglasgow-exts</code> was not an especially elegant solution for people wishing to use nonstandard Haskell, but having to insert <code>LANGUAGE</code> pragmas at the top of every module just to take advantage of the best features GHC has to offer is a burden, and it is unnecessarily intimidating. I think much of the Haskell community finds the use of <code>LANGUAGE</code> pragmas preferable to enabling extensions globally using the <code>default-extensions</code> list in the <code>.cabal</code> file, but I cut across the grain on that issue <em>hard</em>. The vast majority of language extensions I use are extensions I want enabled all the time; a list of them at the top of a module is just distracting noise, and it only serves to bury the extensions I really do want to enable on a module-by-module basis. It also makes it tricky to communicate with a team which extensions are acceptable (or even preferable) and which are discouraged.</p><p>My <em><strong>strong</strong></em> recommendation if you decide to write GHC Haskell on a team is to agree as a group to a list of extensions the team is happy with enabling everywhere and putting those extensions in the <code>default-extensions</code> list in the <code>.cabal</code> file. This eliminates clutter, busywork, and the conceptual overhead of remembering which extensions are in favor, and which are discouraged. This is a net win, and it isn’t at all difficult to look in the <code>.cabal</code> file when you want to know which extensions are in use.</p><p>Now, with that small digression out of the way, the question becomes precisely which extensions should go into that <code>default-extensions</code> list. I happen to like using most of the features GHC makes available, so I enable a whopping <strong>34</strong> language extensions <em>by default</em>. As of GHC 8.2, here is my list:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable"><code>DeriveFoldable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor"><code>DeriveFunctor</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric"><code>DeriveGeneric</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift"><code>DeriveLift</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable"><code>DeriveTraversable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a></p></li></ul><p>This is a lot, and a few of them are likely to be more controversial than others. Since I do not imagine everyone will agree with everything in this list, I’ve broken it down into smaller chunks, arranged from what I think ought to be least controversial to most controversial, along with a little bit of justification why each extension is in each category. If you’re interested in coming up with your own list of extensions, the rest of this section is for you.</p><h3><a name="trivial-lifting-of-standards-imposed-limitations"></a>Trivial lifting of standards-imposed limitations</h3><p>A few extensions are tiny changes that lift limitations that really have no reason to exist, other than that they are mandated by the standard. I am not sure why these restrictions are in the standard to begin with, other than perhaps a misguided attempt at making the language simpler. These extensions include the following:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a></p></li></ul><p>These extensions have no business <em>not</em> being turned on everywhere. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a> end up being turned on in almost any nontrivial Haskell module, since without them, the typeclass system is pointlessly and artificially limited.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a> is extremely useful, completely safe, and has zero downsides.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a> are almost impossible to avoid, given how many libraries use them, and they are a completely obvious generalization of single-parameter typeclasses. Much like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a>, I see no real reason to ever leave these disabled.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a> is even stranger to me, since <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyDataDecls"><code>EmptyDataDecls</code></a> is in Haskell 2010, so it’s possible to define empty datatypes in standard Haskell but not exhaustively pattern-match on them! This is silly, and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a> should be standard Haskell.</p><h3><a name="syntactic-conveniences"></a>Syntactic conveniences</h3><p>A few GHC extensions are little more than trivial, syntactic abbreviations. These things would be tiny macros in a Lisp, but they need to be extensions to the compiler in Haskell:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a></p></li></ul><p>All of these extensions are only triggered by explicit use of new syntax, so existing programs will never change behavior when these extensions are introduced.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a> only saves a few characters, but it eliminates the need to come up with a fresh, unique variable name that will only be used once, which is sometimes hard to do and leads to worse names overall. Sometimes, it really is better to leave something unnamed.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a> isn’t something I find I commonly need, but when I do, it’s nice to have. It’s far easier to read than nested <code>if...then...else</code> chains, and it uses the existing guard syntax already used with function declarations and <code>case...of</code>, so it’s easy to understand, even to those unfamiliar with the extension.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a> avoids headaches and clutter when using Haskell records without the <a href="https://www.reddit.com/r/haskell/comments/6jaa5f/recordwildcards_and_binary_parsing/djd5ugj/">accidental identifier capture issues</a> of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards"><code>RecordWildCards</code></a>. It’s a nice, safe compromise that brings some of the benefits of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards"><code>RecordWildCards</code></a> without any downsides.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a> is a logical generalization of tuple syntax in the same vein as standard operator sections, and it’s quite useful when using applicative notation. I don’t see any reason to not enable it.</p><h3><a name="extensions-to-the-deriving-mechanism"></a>Extensions to the deriving mechanism</h3><p>GHC’s typeclass deriving mechanism is one of the things that makes Haskell so pleasant to write, and in fact I think Haskell would be nearly unpalatable to write without it. Boilerplate generation is a good thing, since it defines operations in terms of a single source of truth, and generated code is code you do not need to maintain. There is rarely any reason to write a typeclass instance by hand when the deriving mechanism will write it automatically.</p><p>These extensions give GHC’s typeclass deriving mechanism more power without any cost. Therefore, I see no reason <em>not</em> to enable them:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable"><code>DeriveFoldable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor"><code>DeriveFunctor</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric"><code>DeriveGeneric</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift"><code>DeriveLift</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable"><code>DeriveTraversable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a></p></li></ul><p>The first five of these simply extend the list of typeclasses GHC knows how to derive, something that will only ever be triggered if the user explicitly requests GHC derive one of those classes. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a> is quite possibly one of the most important extensions in all of Haskell, since it dramatically improves <code>newtype</code>s’ utility. Wrapper types can inherit instances they need without any boilerplate, and making increased type safety easier and more accessible is always a good thing in my book.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a> is new to GHC 8.2, but it finally presents the functionality of GHC’s <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> extension in a useful way. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> is useful when used with certain libraries that use <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a> (discussed later) with <code>GHC.Generics</code> to derive instances of classes without the deriving being baked into GHC. Unfortunately, enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> essentially disables the far more useful <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a>, so I do <em>not</em> recommend enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a>. Fortunately, with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a>, it’s possible to opt into the <code>anyclass</code> deriving strategy on a case-by-case basis, getting some nice boilerplate reduction in the process.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a> is useful when GHC’s deriving algorithms aren’t <em>quite</em> clever enough to deduce the instance context automatically, so it allows specifying it manually. This is only useful in a few small situations, but it’s nice to have, and there are no downsides to enabling it, so it ought to be turned on.</p><h3><a name="lightweight-syntactic-adjustments"></a>Lightweight syntactic adjustments</h3><p>A couple extensions tweak Haskell’s syntax in more substantial ways than things like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a>, but not in a significant enough way for them to really be at all surprising:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a> mirror strictness annotations on datatypes, so they are unlikely to be confusing, and they provide a much more pleasant notation for annotating the strictness of bindings than explicit uses of <code>seq</code>.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a> are also fairly self-explanatory: they’re just like type annotations, but for types instead of values. Writing kind signatures explicitly is usually unnecessary, but they can be helpful for clarity or for annotating phantom types when <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPolyKinds"><code>PolyKinds</code></a> is not enabled. Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a> doesn’t have any adverse effects, so I see no reason not to enable it everywhere.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a> adjusts the syntax of types slightly, allowing operators to be used as type constructors and written infix, which is technically backwards-incompatible, but I’m a little suspicious of anyone using <code>(!@#$)</code> as a type variable (especially since standard Haskell does not allow them to be written infix). This extension is useful with some libraries like <code>natural-transformations</code> that provide infix type constructors, and it makes the type language more consistent with the value language.</p><h3><a name="polymorphic-string-literals"></a>Polymorphic string literals</h3><p>I’m putting this extension in a category all of its own, mostly because I don’t think any other Haskell extensions have quite the same set of tradeoffs:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a></p></li></ul><p>For me, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is not optional. Haskell’s infamous “string problem” (discussed in more detail at the end of this blog post) means that <code>String</code> is a linked list of characters, and all code that cares about performance actually uses <code>Text</code>. Manually invoking <code>pack</code> on every single string literal in a program is just noise, and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> solves that noise.</p><p>That said, I actually find I don’t use the polymorphism of string literals very often, and I’d be alright with monomorphic literals if I could make them <em>all</em> have type <code>Text</code>. Unfortunately, there isn’t a way to do this, so <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is the next best thing, even if it sometimes causes some unnecessary ambiguities that require type annotations to resolve.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is an extension that I use so frequently, in so many modules (especially in my test suites) that I would rather keep it on everywhere so I don’t have to care about whether or not it’s enabled in the module I’m currently writing. On the other hand, it certainly isn’t my favorite language extension, either. I wouldn’t go as far as to call it a necessary evil, since I don’t think it’s truly “evil”, but it does seem to be necessary.</p><h3><a name="simple-extensions-to-aid-type-annotation"></a>Simple extensions to aid type annotation</h3><p>The following two extensions significantly round out Haskell’s language for referring to types, making it much easier to insert type annotations where necessary (for removing ambiguity or for debugging type errors):</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a></p></li></ul><p>That the behavior of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a> is <em>not</em> the default is actually one of the most common gotchas for new Haskellers. Sadly, it can theoretically adjust the behavior of existing Haskell programs, so I cannot include it in the list of trivial changes, but I would argue such programs were probably confusing to begin with, and I have never seen a program in practice that was impacted by that problem. I think leaving <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a> off is much, much more likely to be confusing than turning it on.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a> is largely unrelated, but I include it in this category because it’s quite useful and cooperates well with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a>. Use of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a> makes instantiation much more lightweight than full-blown type annotations, and once again, it has no downsides if it is enabled and unused (since it is a syntactic addition). I recommend enabling it.</p><h3><a name="simple-extensions-to-the-haskell-type-system"></a>Simple extensions to the Haskell type system</h3><p>A few extensions tweak the Haskell type system in ways that I think are simple enough to be self-explanatory, even to people who might not have known they existed. These are as follows:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a> is largely just used to define typeclass aliases, which is both useful and self-explanatory. Unifying the type and constraint language also has the effect of allowing type-level programming with constraints, which is sometimes useful, but far rarer in practice than the aforementioned use case.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a> are uncommon, looking at the average type in a Haskell program, but they’re certainly nice to have when you need them. The idea of pushing <code>forall</code>s further into a type to adjust how variables are quantified is something that I find people find fairly intuitive, especially after seeing them used once or twice, and higher-rank types do crop up regularly, if infrequently.</p><h3><a name="intermediate-syntactic-adjustments"></a>Intermediate syntactic adjustments</h3><p>Three syntactic extensions to Haskell are a little bit more advanced than the ones I’ve already covered, and none of them are especially related:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is, on the surface, simple. It changes <code>do</code> notation to use <code>Applicative</code> operations where possible, which allows using <code>do</code> notation with applicative functors that are not monads, and it also makes operations potentially more performant when <code>(&lt;*&gt;)</code> can be implemented more efficiently than <code>(&gt;&gt;=)</code>. In theory, it sounds like there are no downsides to enabling this everywhere. However, there are are a few drawbacks that lead me to put it so low on this list:</p><ol><li><p>It considerably complicates the desugaring of <code>do</code> blocks, to the point where the algorithm cannot even be easily syntactically documented. In fact, an additional compiler flag, <code>-foptimal-applicative-do</code>, is a way to <em>opt into</em> optimal solutions for <code>do</code> block expansions, tweaking the desugaring algorithm to have an <em>O</em>(<em>n</em><sup>3</sup>) time complexity! This means that the default behavior is guided by a heuristic, and desugaring isn’t even especially predictable. This isn’t necessarily so bad, since it’s really only intended as an optimization when some <code>Monad</code> operations are still necessary, but it does dramatically increase the complexity of one of Haskell’s core forms.</p></li><li><p>The desugaring, despite being <em>O</em>(<em>n</em><sup>2</sup>) by default, isn’t even especially clever. It relies on a rather disgusting hack that recognizes <code>return e</code>, <code>return $ e</code>, <code>pure e</code>, or <code>pure $ e</code> expressions <em>syntactically</em>, and it completely gives up if an expression with precisely that shape is not the final statement in a <code>do</code> block. This is a bit awkward, since it effectively turns <code>return</code> and <code>pure</code> into syntax when before they were merely functions, but that isn’t all. It also means that the following <code>do</code> block is <em>not</em> desugared using <code>Applicative</code> operations:</p><pre><code class="pygments"><span class="kr">do</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">t</span> +<span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="n">z</span></code></pre><p>This will use the normal, monadic desugaring, despite the fact that it is trivially desugared into <code>Applicative</code> operations as <code>foo a b *&gt; bar s t *&gt; baz y z</code>. In order to get <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> to trigger here, the <code>do</code> block must be contorted into the following:</p><pre><code class="pygments"><span class="kr">do</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">t</span> +<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="n">z</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">r</span></code></pre><p>This seems like an odd oversight.</p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> doesn’t seem able to cope with <code>do</code> blocks when <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is enabled. I reported this as <a href="https://ghc.haskell.org/trac/ghc/ticket/14471">an issue on the GHC bug tracker</a>, but it hasn’t received any attention, so it’s not likely to get fixed unless someone takes the initiative to do so.</p></li><li><p>Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> can cause problems with code that may have assumed <code>do</code> would always be monadic, and sometimes, that can cause code that typechecks to lead to an infinite loop at runtime. Specifically, if <code>do</code> notation is used to define <code>(&lt;*&gt;)</code> in terms of <code>(&gt;&gt;=)</code>, enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> will cause the definition of <code>(&lt;*&gt;)</code> to become self-referential and therefore divergent. Fortunately, this issue can be easily mitigated by simply writing <code>(&lt;*&gt;) = ap</code> instead, which is clearer and shorter than the equivalent code using <code>do</code>.</p></li></ol><p>Given all these things, it seems <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is a little too new in a few places, and it isn’t quite baked. Still, I keep it enabled by default. Why? Well, <em>usually</em> it works fine without any problems, and when I run into issues, I can disable it on a per-module basis by writing <code>{-# LANGUAGE NoApplicativeDo #-}</code>. I still find that keeping it enabled by default is fine the vast majority of the time, I just sometimes need to work around the bugs.</p><p>In contrast, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a> isn’t buggy at all, as far as I can tell, it’s just not usually useful without fairly advanced features like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> (for type equalities) or <code>GHC.Generics</code>. I mostly use it for <a href="/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/">making lifting instances for <code>mtl</code>-style typeclasses easier to write</a>, which I’ve found to be a tiny bit tricky to explain (mostly due to the use of type equalities in the context), but it works well. I don’t see any real reason to leave this disabled, but if you don’t think you’re going to use it anyway, it doesn’t really matter one way or the other.</p><p>Finally, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a> allow users to extend the pattern language just as they are allowed to extend the value language. Bidirectional pattern synonyms are isomorphisms, and it’s quite useful to allow those isomorphisms to be used with Haskell’s usual pattern-matching syntax. I think this extension is actually quite benign, but I put it so low on this list because it seems infrequently used, and I get the sense most people consider it fairly advanced. I would argue, however, that it’s a very pleasant, useful extension, and it’s no more complicated than a number of the features in Haskell 98.</p><h3><a name="intermediate-extensions-to-the-haskell-type-system"></a>Intermediate extensions to the Haskell type system</h3><p>Now we’re getting into the meat of things. Everything up to this point has been, in my opinion, completely self-evident in its usefulness and simplicity. As far as I’m concerned, the extensions in the previous six sections have no business ever being left disabled. Starting in this section, however, I could imagine a valid argument being made either way.</p><p>The following three extensions add some complexity to the Haskell type system in return for some added expressive power:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> are related, given that the former is subsumed by the latter, but <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> also enables an alternative syntax. Both syntaxes allow packing away a typeclass dictionary or equality constraint that is brought into scope upon a successful pattern-match against a data constructor, something that is sometimes quite useful but certainly a departure from Haskell’s simple ADTs.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a> extend multi-parameter typeclasses, and they are almost unavoidable, given their use in the venerable <code>mtl</code> library. Like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a>, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a> add an additional layer of complexity to the typeclass system in order to express certain things that would otherwise be difficult or impossible.</p><p>All of these extensions involve a tradeoff. Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> also implies <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds"><code>MonoLocalBinds</code></a>, which disables let generalization, one of the most likely ways a program that used to typecheck might subsequently fail to do so. Some might argue that this is a good reason to turn <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> on in a per-module basis, but I disagree: I actually want my language to be fairly consistent, and given that I know I am likely going to want to use <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> <em>somewhere</em>, I want <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds"><code>MonoLocalBinds</code></a> enabled <em>everywhere</em>, not inconsistently and sporadically.</p><p>That aside, all these extensions are relatively safe. They are well-understood, and they are fairly self-contained extensions to the Haskell type system. I think these extensions have a very good power to cost ratio, and I find myself using them regularly (especially <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a>), so I keep them enabled globally.</p><h3><a name="advanced-extensions-to-the-haskell-type-system"></a>Advanced extensions to the Haskell type system</h3><p>Finally, we arrive at the last set of extensions in this list. These are the most advanced features Haskell’s type system currently has to offer, and they are likely to be the most controversial to enable globally:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a></p></li></ul><p>All of these extensions exist exclusively for the purpose of type-level programming. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a> allows datatype promotion, creating types that are always uninhabited and therefore can only be used phantom. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> allows the definition of type-level functions that map types to other types. Both of these are minor extensions to Haskell’s surface area, but they have rather significant ramifications on the sort of programming that can be done and the way GHC’s typechecker must operate.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> is an interesting extension because it comes in so many flavors: associated type synonyms, associated datatypes, open and closed type synonym families, and open and closed datatype families. Associated types tend to be easier to grok and easier to use, though they can also be replaced by functional dependencies. Open type families are also quite similar to classes and instances, so they aren’t <em>too</em> tricky to understand. Closed type families, on the other hand, are a rather different beast, and they can be used to do fairly advanced things, <em>especially</em> in combination with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a>.</p><p>I happen to appreciate GHC’s support for these features, and while I’m hopeful that an eventual <code>DependentHaskell</code> will alleviate many of the existing infelicities with dependently typed programming in GHC, in the meantime, it’s often useful to enjoy what exists where practically applicable. Therefore, I have little problem keeping them enabled, since, like the vast majority of extensions on this list, these extensions merely lift restrictions, not adjust semantics of the language without the extensions enabled. When I am going to write a type family, I am going to turn on <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a>; I see no reason to annotate the modules in which I decide to do so. I do not write an annotation at the top of each module in which I define a typeclass or a datatype, so why should I do so with type families?</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a> is a little bit different, since it’s a very new extension, and it doesn’t seem to always work as well as I would hope. Still, when it doesn’t work, it fails with a very straightforward error message, and when it works, it is legitimately useful, so I don’t see any real reason to leave it off if <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> is enabled.</p><h3><a name="extensions-intentionally-left-off-this-list"></a>Extensions intentionally left off this list</h3><p>Given what I’ve said so far, it may seem like I would advocate flipping on absolutely every lever GHC has to offer, but that isn’t actually true. There are a few extensions I quite intentionally do <em>not</em> enable.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XUndecidableInstances"><code>UndecidableInstances</code></a> is something I turn on semi-frequently, since GHC’s termination heuristic is not terribly advanced, but I turn it on per-module, since it’s useful to know when it’s necessary (and in application code, it rarely is). <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverlappingInstances"><code>OverlappingInstances</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XIncoherentInstances"><code>IncoherentInstances</code></a>, in contrast, are completely banned—not only are they almost always a bad idea, GHC has a better, more fine-grained way to opt into overlapping instances, using the <code>{-# OVERLAPPING #-}</code>, <code>{-# OVERLAPPABLE #-}</code>, and <code>{-# INCOHERENT #-}</code> pragmas.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XQuasiQuotes"><code>QuasiQuotes</code></a> are tricky ones. Anecdotes seem to suggest that enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> everywhere leads to worse compile times, but after trying this on a few projects and measuring, I wasn’t able to detect any meaningful difference. Unless I manage to come up with some evidence that these extensions actually slow down compile times just by being <em>enabled</em>, even if they aren’t used, then I may add them to my list of globally-enabled extensions, since I use them regularly.</p><p>Other extensions I haven’t mentioned are probably things I just don’t use very often and therefore haven’t felt the need to include on this list. It certainly isn’t exhaustive, and I add to it all the time, so I expect I will continue to do so in the future. This is just what I have for now, and if your favorite extension isn’t included, it probably isn’t a negative judgement against that extension. I just didn’t think to mention it.</p><h2><a name="libraries-a-field-guide"></a>Libraries: a field guide</h2><p>Now that you’re able to build a Haskell project and have chosen which handpicked flavor of Haskell you are going to write, it’s time to decide which libraries to use. Haskell is an expressive programming language, and the degree to which different libraries can shape the way you structure your code is significant. Picking the right libraries can lead to clean code that’s easy to understand and maintain, but picking the wrong ones can lead to disaster.</p><p>Of course, there are <em>thousands</em> of Haskell libraries on Hackage alone, so I cannot hope to cover all of the ones I have ever found useful, and I certainly cannot cover ones that would be useful but I did not have the opportunity to try (of which there are certainly many). This blog post is long enough already, so I’ll just cover a few categories of libraries that I think I can offer interesting commentary on; most libraries can generally speak for themselves.</p><h3><a name="having-an-effect"></a>Having an effect</h3><p>One of the first questions Haskell programmers bump into when they begin working on a large application is how they’re going to model effects. Few practical programming languages are pure, but Haskell is one of them, so there’s no getting away from coming up with a way to manage side-effects.</p><p>For some applications, Haskell’s built-in solution might be enough: <code>IO</code>. This can work decently for data processing programs that do very minimal amounts of I/O, and the types of side-effects they perform are minimal. For these applications, most of the logic is likely to be pure, which means it’s already easy to reason about and easy to test. For other things, like web applications, it’s more likely that a majority of the program logic is going to be side-effectful by its nature—it may involve making HTTP requests to other services, interacting with a database, and writing to logfiles.</p><p>Figuring out how to structure these effects in a type-safe, decoupled, composable way can be tricky, especially since Haskell has so many different solutions. I could not bring myself to choose just one, but I did choose two: the so-called “<code>mtl</code> style” and freer monads.</p><p><code>mtl</code> style is so named because it is inspired by the technique of interlocking monadic typeclasses and lifting instances used to model effects using constraints that is used in the <a href="https://hackage.haskell.org/package/mtl"><code>mtl</code></a> library. Here is a small code example of what <code>mtl</code> style typeclasses and handlers look like:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MaybeT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="p">(</span><span class="s">"readFile: no such file "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="p">)</span> + +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">delete</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="n">vfs</span></code></pre><p>This is the most prevalent way to abstract over effects in Haskell, and it’s been around for a long time. Due to the way it uses the typeclass system, it’s also very fast, since GHC can often specialize and inline the typeclass dictionaries to avoid runtime dictionary passing. The main drawbacks are the amount of boilerplate required and the conceptual difficulty of understanding exactly how monad transformers, monadic typeclasses, and lifting instances all work together to discharge <code>mtl</code> style constraints.</p><p>There are various alternatives to <code>mtl</code>’s direct approach to effect composition, most of which are built around the idea of reifying a computation as a data structure and subsequently interpreting it. The most popular of these is the <code>Free</code> monad, a clever technique for deriving a monad from a functor that happens to be useful for modeling programs. Personally, I think <code>Free</code> is overhyped. It’s a cute, mathematically elegant technique, but it involves a lot of boilerplate, and composing effect algebras is still a laborious process. The additional expressive power of <code>Free</code>, namely its ability to choose an interpreter dynamically, at runtime, is rarely necessary or useful, and it adds complexity and reduces performance for few benefits. (And in fact, this is still possible to do with <code>mtl</code> style, it’s just uncommon because there is rarely any need to do so.)</p><p>A 2017 blog post entitled <a href="https://markkarpov.com/post/free-monad-considered-harmful.html">Free monad considered harmful</a> discussed <code>Free</code> in comparison with <code>mtl</code> style, and unsurprisingly cast <code>Free</code> in a rather unflattering light. I largely agree with everything outlined in that blog post, so I will not retread its arguments here. I do, however, think that there is another abstraction that <em>is</em> quite useful: the so-called “freer monad” used to implement extensible effects.</p><p>Freer moves even further away from worrying about functors and monads, since its effect algebras do not even need to be functors. Instead, freer’s effect algebras are ordinary GADTs, and reusable, composable effect handlers are easily written to consume elements of these datatypes. Unfortunately, the way this works means that GHC is still not clever enough to optimize freer monads as efficiently as <code>mtl</code> style, since it can’t easily detect when the interpreter is chosen statically and use that information to specialize and inline effect implementations, but the cost difference is significantly reduced, and I’ve found that in real application code, the vast majority of the cost does not come from the extra overhead introduced by a more expensive <code>(&gt;&gt;=)</code>.</p><p>There are a few different implementations of freer monads, but I, sadly, was not satisfied with any of them, so I decided to contribute to the problem by creating yet another one. My implementation is called <a href="https://hackage.haskell.org/package/freer-simple"><code>freer-simple</code></a>, and it includes a streamlined API with <a href="https://hackage.haskell.org/package/freer-simple-1.0.1.1/docs/Control-Monad-Freer.html">more documentation than any other freer implementation</a>. Writing the above <code>mtl</code> style example using <code>freer-simple</code> is more straightforward:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="nb">()</span> + +<span class="nf">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Member</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">send</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span> + +<span class="nf">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Member</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">send</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="nf">runFileSystemIO</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LastMember</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span> +<span class="nf">runFileSystemIO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">interpretM</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="kr">case</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="nf">runFileSystemInMemory</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">effs</span> +<span class="nf">runFileSystemInMemory</span><span class="w"> </span><span class="n">initVfs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runState</span><span class="w"> </span><span class="n">initVfs</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">State</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span> +<span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">reinterpret</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">case</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="p">(</span><span class="s">"readFile: no such file "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="p">)</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">delete</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="n">vfs</span></code></pre><p>(It could be simplified further with a little bit of Template Haskell to generate the <code>readFile</code> and <code>writeFile</code> function definitions, but I haven’t gotten around to writing that.)</p><p>So which effect system do I recommend? I used to recommend <code>mtl</code> style, but as of only two months ago, I now recommend <code>freer-simple</code>. It’s easier to understand, involves less boilerplate, achieves “good enough” performance, and generally gets out of the way wherever possible. Its API is designed to make it easy to do the sorts of the things you most commonly need to do, and it provides a core set of effects that can be used to build a real-world application.</p><p>That said, freer is indisputably relatively new and relatively untested. It has success stories, but <code>mtl</code> style is still the approach used by the majority of the ecosystem. <code>mtl</code> style has more library support, its performance characteristics are better understood, and it is a tried and true way to structure effects in a Haskell application. If you understand it well enough to use it, and you are happy with it in your application, my recommendation is to stick with it. If you find it confusing, however, or you end up running up against its limits, give <code>freer-simple</code> a try.</p><h3><a name="through-the-looking-glass-to-lens-or-not-to-lens"></a>Through the looking glass: to lens or not to lens</h3><p>There’s no getting around it: <a href="https://hackage.haskell.org/package/lens"><code>lens</code></a> is a behemoth of a library. For a long time, I wrote Haskell without it, and honestly, it worked out alright. I just wasn’t doing a whole lot of work that involved complicated, deeply-nested data structures, and I didn’t feel the need to bring in a library with such a reputation for having impenetrable operators and an almost equally impenetrable learning curve.</p><p>But, after some time, I decided I wanted to take the plunge. So I braced myself for the worst, pulled out my notebook, and started writing some code. To my surprise… it wasn’t that hard. It made sense. Sure, I still don’t know how it works on the inside, and I never did learn the majority of the exports in <code>Control.Lens.Operators</code>, but I had no need to. Lenses were useful in the way I had expected them to be, and so were prisms. One thing led to another, and before long, I understood the relationship between the various optics, the most notable additions to my toolkit being folds and traversals. Sure, the type errors were completely opaque much of the time, but I was able to piece things together with ample type annotations and time spent staring at ill-typed expressions. Before long, I had developed an intuition for <code>lens</code>.</p><p>After using it for a while, I retrospected on whether or not I liked it, and honestly, I still can’t decide. Some lensy expressions were straightforward to read and were a pleasant simplification, like this one:</p><pre><code class="pygments"><span class="nf">paramSpecs</span><span class="w"> </span><span class="o">^..</span><span class="w"> </span><span class="n">folded</span><span class="o">.</span><span class="n">_Required</span></code></pre><p>Others were less obviously improvements, such as this beauty:</p><pre><code class="pygments"><span class="kt">M</span><span class="o">.</span><span class="n">fromList</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">paramSpecs</span><span class="w"> </span><span class="o">^..</span><span class="w"> </span><span class="n">folded</span><span class="o">.</span><span class="n">_Optional</span><span class="o">.</span><span class="n">filtered</span><span class="w"> </span><span class="p">(</span><span class="n">has</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">_2</span><span class="o">.</span><span class="n">_UsePreviousValue</span><span class="p">)</span></code></pre><p>But operator soup aside, there was something deeper about <code>lens</code> that bothered me, and I just wasn’t sure what. I didn’t know how to articulate my vague feelings until I read a 2014 blog post entitled <a href="https://ro-che.info/articles/2014-04-24-lens-unidiomatic">Lens is unidiomatic Haskell</a>, which includes a point that I think is spot-on:</p><blockquote><p>Usually, types in Haskell are rigid. This leads to a distinctive style of composing programs: look at the types and see what fits where. This is impossible with <code>lens</code>, which takes overloading to the level mainstream Haskell probably hasn’t seen before.</p><p>We have to learn the new language of the <code>lens</code> combinators and how to compose them, instead of enjoying our knowledge of how to compose Haskell functions. Formally, <code>lens</code> types are Haskell function types, but while with ordinary Haskell functions you immediately see from types whether they can be composed, with <code>lens</code> functions this is very hard in practice.</p><p>[…]</p><p>Now let me clarify that this doesn’t necessarily mean that <code>lens</code> is a bad library. It’s an <em>unusual</em> library. It’s almost a separate language, with its own idioms, embedded in Haskell.</p></blockquote><p>The way <code>lens</code> structures its types deliberately introduces a sort of subtyping relationship—for example, all lenses are traversals and all traversals are folds, but not vice versa—and indeed, knowing this subtyping relationship is essential to working with the library and understanding how to use it. It is helpfully documented with a large diagram on <a href="https://hackage.haskell.org/package/lens">the <code>lens</code> package overview page</a>, and that diagram was most definitely an invaluable resource for me when I was learning how to use the library.</p><p>On the surface, this isn’t unreasonable. Subtyping is an enormously useful concept! The only reason Haskell dispenses with it entirely is because it makes type inference notoriously difficult. The subtyping relation between optics is one of the things that makes them so useful, since it allows you to easily compose a lens with a prism and get a traversal out. Unfortunately, the downside of all this is that Haskell does not truly have subtyping, so all of <code>lens</code>’s “types” really must be type aliases for types of roughly the same shape, namely functions. This makes type errors completely <em>baffling</em>, since the errors do not mention the aliases, only the fully-expanded types (which are often rather complicated, and their meaning is not especially clear without knowing how <code>lens</code> works under the hood).</p><p>So the above quote is correct: working with <code>lens</code> really <em>is</em> like working in a separate embedded language, but I’m usually okay with that. Embedded, domain-specific languages are good! Unfortunately, in this case, the host language is not very courteous to its guest. Haskell does not appear to be a powerful enough language for <code>lens</code> to be a language in its own right, so it must piggyback on top of Haskell’s error reporting mechanisms, which are insufficient for <code>lens</code> to be a cohesive linguistic abstraction. Just as debugging code by stepping through the assembly it produces (or, perhaps more relevant in 2018, debugging a compile-to-JS language by looking at the emitted JavaScript instead of the source code) makes for an unacceptably leaky language. We would never stand for such a thing in our general-purpose language tooling, and we should demand better even in our embedded languages.</p><p>That said, <code>lens</code> is just too useful to ignore. It is a hopelessly leaky abstraction, but it’s still an abstraction, and a powerful one at that. Given my selection of default extensions as evidence, I think it’s clear I have zero qualms with “advanced” Haskell; I will happily use even <code>singletons</code> where it makes sense. Haskell’s various language extensions are sometimes confusing in their own right, but their complexity is usually fundamental to the expressive power they bring. <code>lens</code> has some fundamental complexity, too, but it is mostly difficult for the wrong reasons. Still, while it is not the first library I reach for on every new Haskell project, manipulating nested data without <code>lens</code> is just too unpleasant after tasting the nectar, so I can’t advise against it in good faith.</p><p>Sadly, this means I’m a bit wishy-washy when it comes to using <code>lens</code>, but I do have at least one recommendation: if you decide to use <code>lens</code>, it’s better to go all-in. Don’t generate lenses for just a handful of datatypes, do it for <em>all</em> of them. You can definitely stick to a subset of the <code>lens</code> library’s features, but don’t apply it in some functions but not others. Having too many different, equally valid ways of doing things leads to confusion and inconsistency, and inconsistency minimizes code reuse and leads to duplication and spaghetti. Commit to using <code>lens</code>, or don’t use it at all.</p><h3><a name="mitigating-the-string-problem"></a>Mitigating the string problem</h3><p>Finally, Haskell has a problem with strings. Namely, <code>String</code> is a type alias for <code>[Char]</code>, a lazy, singly linked list of characters, which is an awful representation of text. Fortunately, the answer to this problem is simple: ban <code>String</code> in your programs.</p><p>Use <code>Text</code> everywhere. I don’t really care if you pick strict <code>Text</code> or lazy <code>Text</code>, but pick one and stick to it. Don’t ever use <code>String</code>, and <em>especially</em> don’t ever, <em>ever</em>, <em><strong>ever</strong></em> use <code>ByteString</code> to represent text! There are enormously few legitimate cases for using <code>ByteString</code> in a program that is not explicitly about reading or writing raw data, and even at that level, <code>ByteString</code> should only be used at program boundaries. In that sense, I treat <code>ByteString</code> much the same way I treat <code>IO</code>: push it to the boundaries of your program.</p><p>One of Haskell’s core tenets is making illegal states unrepresentable. Strings are not especially useful datatypes for this, since they are sequences of arbitrary length made up of atoms that can be an enormously large number of different things. Still, string types enforce a very useful invariant, a notion of a sequence of human-readable characters. In the presence of Unicode, this is a more valuable abstraction than it might seem, and the days of treating strings as little different from sequences of bytes are over. While strings make a poor replacement for enums, they are quite effective at representing the incredible amount of text humans produce in a staggeringly large number of languages, and they are the right type for that job.</p><p><code>ByteString</code>, on the other hand, is essentially never the right type for any job. If a type classifies a set of values, <code>ByteString</code> is no different from <code>Any</code>. It is the structureless type, the all-encompassing blob of bits. A <code>ByteString</code> could hold anything at all—some text, an image, an executable program—and the type system certainly isn’t going to help to answer that question. The only use case I can possibly imagine for passing around a <code>ByteString</code> in your program rather than decoding it into a more precise type is if it truly holds opaque data, e.g. some sort of token or key provided by a third party with no structure guaranteed whatsoever. Still, even this should be wrapped in a <code>newtype</code> so that the type system enforces this opaqueness.</p><p>Troublingly, <code>ByteString</code> shows up in many libraries’ APIs where it has no business being. In many cases, this seems to be things where ASCII text is expected, but this is hardly a good reason to willingly accept absolutely anything and everything! Make an <code>ASCII</code> type that forbids non-ASCII characters, and provide a <code>ByteString -&gt; Maybe ASCII</code> function. Alternatively, think harder about your problem in question to properly support Unicode as you almost certainly ought to.</p><p>Other places <code>ByteString</code> appears are similarly unfortunate. Base-64 encoding, for example, could be given the wonderfully illustrative type <code>ByteString -&gt; Text</code>, or even <code>ByteString -&gt; ASCII</code>! Such a type makes it immediately clear why base-64 is useful: it allows transforming arbitrary binary data into a reliable textual encoding. If we consider that <code>ByteString</code> is essentially <code>Any</code>, this function has the type <code>Any -&gt; ASCII</code>, which is amazingly powerful! We can convert <em>anything</em> to ASCII text!</p><p>Existing libraries, however, just provide the boring, disappointingly inaccurate type <code>ByteString -&gt; ByteString</code>, which is one of the most useless types there is. It is essentially <code>Any -&gt; Any</code>, the meaningless function type. It conveys nothing about what it does, other than that it is pure. Giving a function this type is scarcely better than dynamic typing. Its mere existence is a failure of Haskell library design.</p><p>But wait, it gets worse! <code>Data.Text.Encoding</code> exports a function called <code>decodeUtf8</code>, which has type <code>ByteString -&gt; Text</code>. What an incredible function with a captivating type! Whatever could it possibly do? Again, this function’s type is basically <code>Any -&gt; Text</code>, which is remarkable in the power it gives us. Let’s try it out, shall we?</p><pre><code>ghci&gt; decodeUtf8 "\xc3\x28" +"*** Exception: Cannot decode byte '\x28': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream +</code></pre><p>Oh. Well, that’s a disappointment.</p><p>Haskell’s string problem goes deeper than <code>String</code> versus <code>Text</code>; it seems to have wound its way around the collective consciousness of the Haskell community and made it temporarily forget that it cares about types and totality. This isn’t that hard, I swear! I can only express complete befuddlement at how many of these APIs are just completely worthless.</p><p>Fortunately, there is a way out, and that way out is <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a>. It is the first Haskell library I ever wrote. It provides <em>type safe</em>, <em>total</em> conversions between <code>Text</code> and various other types, and it is encoding aware. It provides appropriately-typed base-16 and base-64 conversion functions, and is guaranteed to never raise any exceptions. Use it, and apply the Haskell philosophy to your strings, just as you already do for everything else in your program.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p><em>Phew.</em></p><p>When I started writing this blog post, it used the phrase “short overview” in the introduction. It is now over ten thousand words long. I think that’s all I have it in me to say for now.</p><p>Haskell is a wonderful language built by a remarkable group of people. Its community is often fraught with needlessly inflammatory debates about things like the value of codes of conduct, the evils of Hackage revisions, and precisely how much or how little people ought to care about the monad laws. These flame wars frustrate me to no end, and they sometimes go so far as to make me ashamed to call myself a part of the Haskell community. Many on the “outside” seem to view Haskellers as an elitist, mean-spirited cult, more interested in creating problems for itself than solving them.</p><p>That perception is categorically wrong.</p><p>I have never been in a community of programmers so dedicated and passionate about applying thought and rigor to building software, then going out and <em>actually doing it</em>. I don’t know anywhere else where a cutting-edge paper on effect systems is discussed by the very same people who are figuring out how to reliably deploy distributed services to AWS. Some people view the Haskell community as masturbatory, and to some extent, they are probably right. One of my primary motivators for writing Haskell is that it is fun and it challenges me intellectually in ways that other languages don’t. But that challenge is not a sign of uselessness, it is a sign that Haskell is <em>so close</em> to letting me do the right thing, to solving the problem the right way, to letting me work without compromises. When I write in most programming languages, I must constantly accept that my program will never be robust in all the ways I want it to be, and I might as well give up before I even start. Haskell’s greatest weakness is that it tempts me to try.</p><p>Haskell is imperfect, as it will always be. I doubt I will ever be satisfied by any language or any ecosystem. There will always be more to learn, more to discover, better tools and abstractions to develop. Many of them will not look anything like Haskell; they may not involve formal verification or static types or effect systems at all. Perhaps live programming, structural editors, and runtime hotswapping will finally take over the world, and we will find that the problems we thought we were solving were irrelevant to begin with. I can’t predict the future, and while I’ve found great value in the Haskell school of program construction, I dearly hope that we do not develop such tunnel vision that we cannot see that there may be other ways to solve these problems. Many of the solutions are things we likely have not even begun to think about. Still, whether that happens or not, it is clear to me that Haskell is a point in the design space unlike any other, and we learn almost as much from the things it gets wrong as we do from the things it gets right.</p><p>It’s been a wonderful two years, Haskell. I won’t be a stranger.</p><ol class="footnotes"></ol></article>A space of their own: adding a type namespace to Hackett2017-10-27T00:00:00Z2017-10-27T00:00:00ZAlexis King<article><p>As previously discussed on this blog, <a href="https://github.com/lexi-lambda/hackett">my programming language, Hackett</a>, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?</p><p>For now, at least, the answer is that Hackett will emulate Haskell: <strong>Hackett now has two namespaces</strong>. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.</p><h2><a name="why-two-namespaces"></a>Why two namespaces?</h2><p>Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.</p><p>This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="ss">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="ss">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="ss">c</span><span class="p">)))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly <em>annotate</em> when they wish to use a value from a different namespace:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">mapcar</span><span class="w"> </span><span class="nf">#&#39;</span><span class="nb">car</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="nv">c</span><span class="p">)))</span> +<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>The Common Lisp <code>#'x</code> reader abbreviation is equivalent to <code>(function x)</code>, and <code>function</code> is a special form that references a value in the function namespace.</p><p>While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.</p><p>However, Hackett is a little different from all of the aforementioned languages because Hackett has <em>types</em>. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is <em>always</em> syntactically unambiguous.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Even if types and values live in separate namespaces, there is no need for a <code>type</code> form a la CL’s <code>function</code> because it can always be determined implicitly.</p><p>For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span></code></pre><p>This defines a binding named <code>Tuple</code> at the type level, which is a <em>type constructor</em> of two arguments that produces a type of kind <code>*</code>,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> and another binding named <code>Tuple</code> at the value level, which is a <em>value constructor</em> of two arguments that produces a value of type <code>(Tuple a b)</code>.</p><p>But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor <code>tuple</code> instead of <code>Tuple</code>? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>This works fine. But what happens if the programmer decides to change the name of the <code>bar</code> value?</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">qux</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>Can you spot the bug? Disturbingly, this code <em>still compiles</em>! Even though <code>bar</code> is not a member of <code>Foo</code> anymore, it’s still a valid pattern, since names used as patterns match anything, just as the <code>y</code> pattern matches against any integer inside the <code>baz</code> constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: <code>(foo-&gt;integer (baz 42))</code> will still produce <code>0</code>, not <code>42</code>, since the first case always matches.</p><p>Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the <code>Tuple</code> example above, which is illegal if a programming language only supports a single namespace.</p><p>Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The <code>Tuple</code> example from above is perfectly legal Hackett.</p><h2><a name="adding-namespaces-to-a-language"></a>Adding namespaces to a language</h2><p>Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to <em>implement</em> such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?</p><p>Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: <strong>the remainder of this blog post is <em>highly technical</em>, and some of it involves some of the more esoteric corners of Racket’s macro system</strong>. This blog post is <em>not</em> representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.</p><h3><a name="namespaces-as-scopes"></a>Namespaces as scopes</h3><p>With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as <a href="https://www.cs.utah.edu/plt/scope-sets/"><em>sets of scopes</em></a>, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.</p><p>Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the <em>value scope</em>, and all identifiers in type position must have a different scope, which we will call the <em>type scope</em>. How do we create these scopes and apply them to identifiers? In Racket, we use a function called <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29"><code>make-syntax-introducer</code></a>, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can <em>add</em> the scope to all pieces of the syntax object, <em>remove</em> the scope, or <em>flip</em> the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call <code>make-syntax-introducer</code> once for each namespace:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>We define these in a <code>begin-for-syntax</code> block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Each of these two forms is like <code>begin</code>, which is a Racket form that is, for our purposes, essentially a no-op, but it applies <code>value-introducer</code> or <code>type-introducer</code> to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">value-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">type-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span></code></pre><p>This program produces the following output:</p><pre><code>'value-x +'type-x +</code></pre><p>It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.</p><p>However, although this is close, it isn’t <em>quite</em> right. What happens if we nest the two inside each other?</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span></code></pre><pre><code>x: identifier's binding is ambiguous + context...: + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) + #(190359 intdef) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) +</code></pre><p>Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of <code>x</code> is <em>ambiguous</em> because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of <code>begin/value</code> or <code>begin/type</code> to <em>override</em> outer ones, ensuring that a use can only be in a single namespace at a time.</p><p>To do this, we simply need to adjust <code>begin/value</code> and <code>begin/type</code> to remove the other scope in addition to adding the appropriate one:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Now our nested program runs, and it produces <code>'type-x</code>, which is exactly what we want—the “nearest” scope wins.</p><p>With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.</p><h3><a name="namespaces-that-cross-module-boundaries"></a>Namespaces that cross module boundaries</h3><p>The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.</p><p>Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the <em>other</em> module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export <em>symbols</em>, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named <code>foo</code>, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the <code>require</code> form’s job to attach the correct scopes to imported identifiers.</p><p>This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup> This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary <em>points</em> of having multiple namespaces.</p><p>What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s <code>require</code> and <code>provide</code> forms are extensible, it’s even possible to implement this mangling in a completely invisible way.</p><p>Currently, the scheme that Hackett uses is to prefix <code>#%hackett-type:</code> onto the beginning of any type exports. This can be defined in terms of a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29"><em>provide pre-transformer</em></a>, which is essentially a macro that cooperates with Racket’s <code>provide</code> form to control the export process. In this case, we can define our <code>type-out</code> provide pre-transformer in terms of <a href="https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29"><code>prefix-out</code></a>, a form built-in to Racket that allows prefixing the names of exports:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">type-out</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-provide-pre-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="w"> </span><span class="n">modes</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">pre-expand-export</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">prefix-out</span><span class="w"> </span><span class="n">#%hackett-type:</span> +<span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">type-introducer</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-out</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span> +<span class="w"> </span><span class="n">modes</span><span class="p">)]))))</span></code></pre><p>Note that we call <code>type-introducer</code> in this macro! That’s because we want to ensure that, when a user writes <code>(provide (type-out Foo))</code>, we look for <code>Foo</code> in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that <code>provide</code> knows <em>which</em> <code>Foo</code> is being provided.</p><p>Once we have referenced the correct binding, the use of <code>prefix-out</code> will appropriately add the <code>#%hackett-type:</code> prefix, so the exporting side is already done. Users do need to explicitly write <code>(type-out ....)</code> if they are exporting a particular type-level binding, but this is rarely necessary, since most users use <code>data</code> or <code>class</code> to export datatypes or typeclasses respectively, which can be modified to use <code>type-out</code> internally. Very little user code actually needs to change to support this adjustment.</p><p>Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the <code>#%hackett-type:</code> prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.</p><p>Just as we extended <code>provide</code> with a provide pre-transformer, we can extend <code>require</code> using a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29"><em>require transformer</em></a>. In code, this entire process looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">and~&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">regexp-match</span><span class="w"> </span><span class="sr">#rx"^#%hackett-type:(.+)$"</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="nb">second</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">unmangle-types-in</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-require-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">imports</span><span class="w"> </span><span class="n">sources</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">expand-import</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-in</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">match-lambda</span> +<span class="w"> </span><span class="p">[(</span><span class="k">and</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="n">local-id</span><span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">local-name</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol-&gt;string</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">local-id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">unmangled-type-name</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">local-name</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">unmangled-type-name</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">unmangled-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;symbol</span><span class="w"> </span><span class="n">unmangled-type-name</span><span class="p">)</span> +<span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="n">local-id</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">unmangled-id</span><span class="p">)</span> +<span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="n">i</span><span class="p">))])</span> +<span class="w"> </span><span class="n">imports</span><span class="p">)</span> +<span class="w"> </span><span class="n">sources</span><span class="p">)])))</span></code></pre><p>This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:</p><ol><li><p>It iterates over each import and calls <code>unmangle-type-name</code> on the imported symbol. If the result is <code>#f</code>, that means the import does not have the <code>#%hackett-type:</code> prefix, and it can be safely passed through unchanged.</p></li><li><p>If <code>unmangle-type-name</code> does <em>not</em> return <code>#f</code>, then it returns the unprefixed name, which is then provided to <code>datum-&gt;syntax</code>, which allows users to forge new identifiers in an <em>unhygienic</em> (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from <code>unmangle-type-name</code>, but with the lexical context of the original identifier.</p></li><li><p>Finally, we pass the new identifier to <code>type-introducer</code> to properly add the type scope, injecting the fresh binding into the type namespace.</p></li></ol><p>With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write <code>type-out</code> when exporting types, it is unlikely that users will want to write <code>unmangle-types-in</code> around each and every import in their program. For that reason, we can define a slightly modified version of <code>require</code> that implicitly wraps all of its subforms with <code>unmangle-types-in</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="p">(</span><span class="k">rename-out</span><span class="w"> </span><span class="p">[</span><span class="n">require/unmangle</span><span class="w"> </span><span class="k">require</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">require/unmangle</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-types-in</span><span class="w"> </span><span class="n">require-spec</span><span class="p">)</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>…and we’re done. Now, Hackett modules can properly import and export type-level bindings.</p><h3><a name="namespaces-plus-submodules-the-devil-s-in-the-details"></a>Namespaces plus submodules: the devil’s in the details</h3><p>Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of <em>submodules</em>.</p><p>Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.</p><p>Submodules in Racket come in two flavors: <code>module</code> and <code>module*</code>. The difference is what order, semantically, they are defined in. Submodules defined with <code>module</code> are essentially defined <em>before</em> their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with <code>module*</code> are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.</p><p>How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.</p><p>However, there is <a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29">a special sort of <code>module*</code> submodule that uses <code>#f</code> in place of a module language</a>, which gives a module access to <em>all</em> of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a <code>test</code> submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="c1">; not provided</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span> + +<span class="p">(</span><span class="k">module*</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">rackunit</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">check-equal?</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="mi">41</span><span class="p">)</span><span class="w"> </span><span class="mi">42</span><span class="p">))</span></code></pre><p>It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>Remember that <code>make-syntax-introducer</code> is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module <a href="https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29">instantiation</a>, as ensured by Racket’s <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">separate compilation guarantee</a>. This means that each module gets its <em>own</em> pair of scopes. This means the body of a <code>module*</code> submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.</p><p>Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we <em>can</em> preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29"><code>make-syntax-delta-introducer</code></a>, we can create a syntax introducer the adds or removes the <em>difference</em> between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for <code>value-introducer</code> and <code>type-introducer</code> to always operate on the same scopes on each module instantiation:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-value/type-introducers</span> +<span class="w"> </span><span class="n">value-introducer:id</span><span class="w"> </span><span class="n">type-introducer:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">scopeless-id</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">introducer-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">value-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">type-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">type-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-value/type-introducers</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="n">type-introducer</span><span class="p">)</span></code></pre><p>The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. <em>However</em>, code inside <code>begin-for-syntax</code> blocks is still re-evaluated every time the module is instantiated! This means we are <em>not</em> circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.</p><p>We still use <code>make-syntax-introducer</code> to create our two scopes, but critically, we only call <code>make-syntax-introducer</code> inside the <code>define-value/type-introducers</code> macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds <code>value-id</code> and <code>type-id</code> as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use <code>make-syntax-delta-introducer</code> to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.</p><p>By guaranteeing each namespace’s scope is always the same, even for different modules, <code>module*</code> submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.</p><h3><a name="the-final-stretch-making-scribble-documentation-namespace-aware"></a>The final stretch: making Scribble documentation namespace-aware</h3><p>As discussed in <a href="/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/">my previous blog post</a>, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.</p><p>In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!</p><p>Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.</p><p>To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with <code>t:</code> upon import, and they are bound to Scribble <a href="https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29"><em>element transformers</em></a> that indicate they should be typeset without the <code>t:</code> prefix. Fortunately, Scribble’s documentation forms <em>do</em> understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.</p><p>In practice, this means Hackett documentation must now include a proliferation of <code>t:</code> prefixes. For example, here is the code for a typeset REPL interaction:</p><pre><code class="pygments"><span class="n">@</span><span class="p">(</span><span class="n">hackett-examples</span> +<span class="w"> </span><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">square</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">t:-&gt;</span><span class="w"> </span><span class="n">t:Integer</span><span class="w"> </span><span class="n">t:Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">[[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="p">}])</span> +<span class="w"> </span><span class="p">(</span><span class="n">square</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span></code></pre><p>Note the use of <code>t:-&gt;</code> and <code>t:Integer</code> instead of <code>-&gt;</code> and <code>Integer</code>. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.</p><p>This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides <code>deftype</code> and <code>deftycon</code> forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to <code>t:</code>-prefixed identifiers to properly index documented forms. Similarly, <code>defdata</code> and <code>defclass</code> have been updated with an understanding of types.</p><p>The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of <code>defform</code> slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into <code>t:</code>-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.</p><h2><a name="analysis-and-unsolved-problems"></a>Analysis and unsolved problems</h2><p>When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).</p><p>For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom <code>require</code> form, types provided by a module’s <em>language</em> are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike <code>require</code>’s require transformers.</p><p>To circumvent this restriction, <code>#lang hackett</code>’s reader includes a somewhat ad-hoc solution that actually inserts a <code>require</code> into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.</p><p>That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do <em>any</em> special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully <a href="http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html">internalizes extra-linguistic mechanisms</a>.</p><p>As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:</p><ul><li><p><strong>Multi-parameter typeclasses</strong> are implemented, along with <strong>default typeclass method implementations</strong>.</p></li><li><p>Pattern-matching performs basic <strong>exhaustiveness checking</strong>, so unmatched cases are a compile-time error.</p></li><li><p>Hackett ships with a <strong>larger standard library</strong>, including an <code>Either</code> type and appropriate functions, an <code>Identity</code> type, a <code>MonadTrans</code> typeclass, and the <code>ReaderT</code> and <code>ErrorT</code> monad transformers.</p></li><li><p><strong>More things are documented</strong>, and parts of the documentation are slightly improved. Additionally, <strong>Hackett’s internals are much more heavily commented</strong>, hopefully making the project more accessible to new contributors.</p></li><li><p><strong>Parts of the typechecker are dramatically simplified</strong>, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.</p></li><li><p>As always, various bug fixes.</p></li></ul><p>Finally, special mention to two new contributors to Hackett, <a href="https://github.com/iitalics">Milo Turner</a> and <a href="https://github.com/Shamrock-Frost">Brendan Murphy</a>. Also special thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> and <a href="https://github.com/michaelballantyne">Michael Ballantyne</a> for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.</p><ol class="footnotes"><li id="footnote-1"><p>“But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have <code>#lang</code>. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate <code>#lang</code>, not a modified version of Hackett, so Hackett can optimize its user experience for what it <em>is</em>, not what it might be someday. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow <code>*</code> to be used to mean “type” at the kind level and “multiply” at the value level. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, <em>per phase</em>, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Hackett progress report: documentation, quality of life, and snake2017-08-28T00:00:00Z2017-08-28T00:00:00ZAlexis King<article><p>Three months ago, <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">I wrote a blog post describing my new, prototype implementation of my programming language, Hackett</a>. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/"><strong>the first (albeit quite incomplete) approach to Hackett’s documentation</strong></a>.</p><p>I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.</p><h2><a name="a-philosophy-of-documentation"></a>A philosophy of documentation</h2><p>Racket, as a project, has always had <a href="http://docs.racket-lang.org">wonderful documentation</a>. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably <a href="http://docs.racket-lang.org/scribble/index.html">Scribble, the Racket documentation tool</a>. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with <a href="http://docs.racket-lang.org/scribble/plt-manuals.html">a powerful library for documenting Racket code</a>. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.</p><p>Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an <code>@</code> character instead of <code>\</code>. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use <a href="http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29">the provided <code>defproc</code> form</a>:</p><pre><code class="pygments"><span class="n">@defproc</span><span class="p">[(</span><span class="nb">add1</span><span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="nb">number?</span><span class="p">])</span><span class="w"> </span><span class="nb">number?</span><span class="p">]{</span> +<span class="n">Returns</span><span class="w"> </span><span class="n">@racket</span><span class="p">[(</span><span class="nb">+</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="mi">1</span><span class="p">)]</span><span class="o">.</span><span class="p">}</span></code></pre><p>This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into <a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29">something like this</a>:</p><p><a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29"></a></p><p>The fact that Scribble documents are fully-fledged <em>programs</em> equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is <a href="http://docs.racket-lang.org/scribble/eval.html">the <code>scribble/example</code> module</a>, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special <code>eval:check</code> form that enables <a href="https://docs.python.org/3/library/doctest.html">doctest</a>-like checking, which allows documentation to serve double duty as a test suite.</p><p>Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is <em>designed</em> with the goal of supporting many different programming languages, and Scribble is no exception. Things like <a href="http://docs.racket-lang.org/scribble/eval.html"><code>scribble/example</code></a> essentially work out of the box with Hackett, and most of <a href="http://docs.racket-lang.org/scribble/plt-manuals.html"><code>scribble/manual</code></a> can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The <code>defproc</code> and <code>defstruct</code> forms are hardly builtins; they are defined as part of the <code>scribble/manual</code> library in terms of Scribble primitives, and <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt">we can do the same</a>.</p><p>Hackett’s documentation already defines three new forms, <code>defdata</code>, <code>defclass</code>, and <code>defmethod</code>, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the <code>Functor</code> typeclass using <code>defclass</code> like this:</p><pre><code class="pygments"><span class="n">@defclass</span><span class="p">[(</span><span class="n">Functor</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="nb">map</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="p">)})]]{</span> + +<span class="n">A</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">@deftech</span><span class="p">{</span><span class="n">functors</span><span class="p">}</span><span class="o">,</span><span class="w"> </span><span class="n">essentially</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="k">provide</span><span class="w"> </span><span class="n">a</span> +<span class="n">mapping</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">“piercing”</span><span class="w"> </span><span class="n">operation.</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">@racket</span><span class="p">[</span><span class="nb">map</span><span class="p">]</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">viewed</span><span class="w"> </span><span class="n">in</span> +<span class="n">different</span><span class="w"> </span><span class="n">ways:</span> + +<span class="k">...</span><span class="p">}</span></code></pre><p>With only a little more than the above code, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29">Hackett’s documentation includes a beautifully-typeset definition of the <code>Functor</code> typeclass</a>, including examples and rich prose:</p><p><a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29"></a></p><p>Scribble makes Hackett’s documentation shine.</p><h3><a name="a-tale-of-two-users"></a>A tale of two users</h3><p>For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.</p><p>A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.</p><p>However, for experienced users, an authoritative reference is <em>exactly</em> what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.</p><p>This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:</p><ul><li><p>Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.</p><p><a href="https://docs.oracle.com/javase/8/docs/api/">Java’s API documentation</a> documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly <em>small</em> language that does not often change.</p><p>On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java <em>does</em> provide guide-style documentation in the form of the <a href="https://docs.oracle.com/javase/tutorial/">Java Tutorials</a>, but they are of inconsistent quality.</p><p>More importantly, while the Java tutorials link to the API docs, the reverse is <strong>not</strong> true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.</p><p>Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.</p></li><li><p>Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.</p><p>The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is <a href="https://developer.mozilla.org/en-US/">MDN</a>. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.</p><p>The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.</p><p>This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. <a href="http://ramdajs.com/docs/">Ramda’s documentation is nothing but a reference</a>, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, <a href="http://passportjs.org/docs">Passport’s docs are essentially <em>only</em> a set of tutorials</a>, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method <em>does</em>. Fortunately, <a href="https://facebook.github.io/react/docs/hello-world.html">there are some libraries, like React</a>, that absolutely <em>nail</em> this, and they have both styles of documentation that are <strong>actually cross-referenced</strong>. Unfortunately, those are mostly the exceptions, not the norm.</p></li><li><p><a href="https://docs.python.org/3/index.html">Python’s documentation is interesting</a>, since it includes a set of tutorials alongside the API reference, and it <em>also</em> ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on <code>if</code> links to the section in the reference about <code>if</code>, but nothing goes in the other direction, which is something of a missed opportunity.</p></li><li><p><a href="https://hackage.haskell.org/package/base">Haskell manages to be especially bad here</a> (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.</p><p>It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and <a href="https://www.haskell.org/haddock/">Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look</a>.</p></li></ul><p>The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with <em>two</em> pieces of documentation: the <a href="http://docs.racket-lang.org/guide/index.html">Racket Guide</a> and the <a href="http://docs.racket-lang.org/reference/index.html">Racket Reference</a>. The guide includes over <strong>one hundred thousand</strong> words of explanations and examples, and the reference includes roughly <strong>half a million</strong>. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.</p><p>Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the <a href="http://docs.racket-lang.org/hackett/guide.html">Hackett Guide</a> and the <a href="http://docs.racket-lang.org/hackett/reference.html">Hackett Reference</a>, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily <em>need</em> to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a <em>lot</em> more accessible without making it any less useful for power users.</p><h2><a name="rounding-hackett-s-library-sanding-its-edges"></a>Rounding Hackett’s library, sanding its edges</h2><p>One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a <em>lot</em> of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.</p><p>Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:</p><ul><li><p>Hackett includes a <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29"><code>Double</code></a> type for working with IEEE 754 double-precision floating-point numbers.</p></li><li><p>Local definitions are supported via the <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29"><code>let</code></a> and <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29"><code>letrec</code></a> forms.</p></li><li><p>The prelude includes many more functions, especially <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29">functions on lists</a>.</p></li><li><p>The Hackett reader has been adjusted to support using <code>.</code> as a bare symbol, since <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29"><code>.</code> is the function composition operator</a>.</p></li><li><p>The Hackett REPL supports many more forms, including <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29">ADT</a>, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29">class</a>, and <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29">instance</a> definitions. Additionally, the REPL now uses <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a> instances to display the results of expressions. To compensate for the inability to print non-<a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a>able things, a new <code>(#:type expr)</code> syntax is permitted to print the type of <em>any</em> expression.</p></li><li><p>Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.</p></li></ul><p>Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189">heavily commented</a> with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.</p><p>One of the most exciting things about documenting Hackett has been realizing just <em>how much</em> already exists. Seriously, if you have gotten to this point in the blog post but haven’t read <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/">the actual documentation</a> yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for <em>me</em>: it’s a language I’d like to use.</p><p>Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!</p><h2><a name="a-demo-with-pictures"></a>A demo with pictures</h2><p>Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?</p><p>Hackett ships with a special package of demo libraries in the aptly-named <code>hackett-demo</code> package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">the previous Hackett blog post</a>, I demonstrated the capabilities of <code>hackett/demo/web-server</code>. In this blog post, we’re going to use <code>hackett/demo/pict</code> and <code>hackett/demo/pict/universe</code>, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!</p><p>As always, we’ll start with <code>#lang hackett</code>, and we’ll import the necessary libraries:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/pict</span> +<span class="w"> </span><span class="n">hackett/demo/pict/universe</span><span class="p">)</span></code></pre><p>With that, we can start immediately with a tiny example. Just to see how <code>hackett/demo/pict</code> works, let’s start by rendering a red square. We can do this by writing a <code>main</code> action that calls <code>print-pict</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))))</span></code></pre><p>If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!</p><p></p><p>Using the REPL, we can inspect the type of <code>print-pict</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">print-pict</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Unit</span><span class="p">))</span></code></pre><p>Unsurprisingly, displaying a picture to the screen needs <code>IO</code>. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of <code>filled-square</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">filled-square</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">Pict</span><span class="p">)</span></code></pre><p>No <code>IO</code> to be seen! This is because “picts” are entirely <em>pure</em> values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">{(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))</span> +<span class="w"> </span><span class="n">hc-append</span> +<span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))}))</span></code></pre><p>This code will print out a red square to the left of a blue one.</p><p></p><p>Again, <code>hc-append</code> is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">hc-append</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="n">Pict</span><span class="p">))</span></code></pre><p>Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!</p><h3><a name="implementing-a-snake-clone"></a>Implementing a snake clone</h3><p>This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the <code>pict</code> or <code>universe</code> libraries. Hopefully it’s still illustrative.</p><p>We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an <code>Eq</code> instance for our points.</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="n">d:left</span><span class="w"> </span><span class="n">d:right</span><span class="w"> </span><span class="n">d:up</span><span class="w"> </span><span class="n">d:down</span><span class="p">)</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">Eq</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="k">==</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)]</span><span class="w"> </span><span class="p">{{</span><span class="n">a</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">c</span><span class="p">}</span><span class="w"> </span><span class="n">&amp;&amp;</span><span class="w"> </span><span class="p">{</span><span class="n">b</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">d</span><span class="p">}})])</span></code></pre><p>With these two datatypes, we can implement a <code>move</code> function that accepts a point and a direction and produces a new point for an adjacent tile:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Direction</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:left</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:right</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:up</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">})]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:down</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">})])</span></code></pre><p>The next step is to define a type for our world state. The <code>big-bang</code> library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span> +<span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="c1">; snake direction</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; snake blocks</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; food blocks</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">set-ws-direction</span><span class="w"> </span><span class="p">[[</span><span class="n">d</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)])</span></code></pre><p>Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-width</span><span class="w"> </span><span class="mi">50</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-height</span><span class="w"> </span><span class="mi">30</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="p">{(</span><span class="n">d*</span><span class="w"> </span><span class="mf">15.0</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">integer-&gt;double</span><span class="p">})</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="p">(</span><span class="n">blank-rect</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-height</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">13.0</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="n">block</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="n">block</span><span class="p">))</span></code></pre><p>Now we can write our actual <code>render</code> function. To do this, we simply need to render each <code>Point</code> in our <code>World-State</code>’s two lists as a block on an <code>empty-board</code>. We’ll write a helper function, <code>render-on-board</code>, which does exactly that:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render-on-board</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Pict</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">pict</span><span class="w"> </span><span class="n">points</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">foldr</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">acc</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">pict</span><span class="p">))</span> +<span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="n">points</span><span class="p">)])</span></code></pre><p>This function uses <code>foldr</code> to collect each point and place the provided pict at the right location using <code>pin-over</code> on an empty board. Using <code>render-on-board</code>, we can write the <code>render</code> function in just a couple of lines:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)</span> +<span class="w"> </span><span class="mf">0.0</span><span class="w"> </span><span class="mf">0.0</span> +<span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="n">food-points</span><span class="p">))])</span></code></pre><p>Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the <code>big-bang</code> library provides a <code>random-integer</code> function, which we can use to write a <code>random-point</code> action:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">random-point</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">point</span><span class="w"> </span><span class="n">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span> +<span class="w"> </span><span class="n">&lt;*&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-height</span><span class="p">)})</span></code></pre><p>Hackett supports applicative notation using infix operators, so <code>random-point</code> looks remarkably readable. It also runs in <code>IO</code>, since the result is, obviously, random. Fortunately, the <code>on-tick</code> function runs in <code>IO</code> as well (unlike <code>render</code>, which must be completely pure), so we can use <code>random-point</code> when necessary to generate a new food block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">init!</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)})</span> +<span class="w"> </span><span class="p">{</span><span class="nb">reverse</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tail!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="nb">reverse</span><span class="p">})</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">new-snake-point</span><span class="w"> </span><span class="p">(</span><span class="n">move</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">(</span><span class="n">head!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">elem?</span><span class="w"> </span><span class="n">food-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">random-point</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">snake-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">delete</span><span class="w"> </span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">food-points</span><span class="p">)})))</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">init!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)}</span> +<span class="w"> </span><span class="n">food-points</span><span class="p">))))])</span></code></pre><p>This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to <code>new-snake-point</code>, then checks if there is a food block at that location. If there is, it generates a <code>new-food-point</code>, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.</p><p>The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our <code>set-ws-direction</code> function that we wrote earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-key</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">KeyEvent</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:left</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:left</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:right</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:right</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:up</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:up</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:down</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:down</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="k">_</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id</span><span class="p">}])</span></code></pre><p>The <code>on-key</code> function runs in <code>IO</code>, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in <code>pure</code>.</p><p>We’re almost done now—all we need to do is set up the <em>initial</em> state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">initial-state</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">initial-food</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">sequence</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="n">repeat</span><span class="w"> </span><span class="n">random-point</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d:right</span> +<span class="w"> </span><span class="p">{(</span><span class="n">point</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">24</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">23</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">nil</span><span class="p">}</span> +<span class="w"> </span><span class="n">initial-food</span><span class="p">))))</span></code></pre><p>Notably, we can use the <code>repeat</code> function to create an infinite list of <code>random-point</code> actions, <code>take</code> the first five of them, then call <code>sequence</code> to execute them from left to right. Now, all we have to do is put the pieces together in a <code>main</code> block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">state</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">initial-state</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">big-bang</span><span class="w"> </span><span class="n">state</span> +<span class="w"> </span><span class="kd">#:to-draw</span><span class="w"> </span><span class="n">render</span> +<span class="w"> </span><span class="kd">#:on-tick</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="mf">0.2</span> +<span class="w"> </span><span class="kd">#:on-key</span><span class="w"> </span><span class="n">on-key</span><span class="p">)))</span></code></pre><p>And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.</p><p></p><h2><a name="contributing-to-hackett"></a>Contributing to Hackett</h2><p>If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.</p><p>On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, <a href="https://github.com/lexi-lambda/hackett/issues">Hackett has an issue tracker</a>, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.</p><p>Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to <a href="https://github.com/lexi-lambda/hackett/issues">open an issue on GitHub</a>, send me a message on the <code>#racket</code> IRC channel on Freenode, or ping me on <a href="http://racket-slack.herokuapp.com">the Racket Slack team</a>.</p><h2><a name="acknowledgements"></a>Acknowledgements</h2><p>Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to <a href="https://github.com/gelisam">Samuel Gélineau, aka gelisam</a>, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.</p><p>Additionally, I want to specially thank <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.</p><p>Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.</p><ol class="footnotes"></ol></article>User-programmable infix operators in Racket2017-08-12T00:00:00Z2017-08-12T00:00:00ZAlexis King<article><p>Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in <a href="https://github.com/lexi-lambda/hackett">Hackett</a>, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.</p><p>Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done <em>without</em> modifying the stock <code>#lang racket</code> reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.</p><h2><a name="our-mission"></a>Our mission</h2><p>Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we <em>do</em> want:</p><ul><li><p>Infix operators should be user-extensible, not limited to a special set of built-in operators.</p></li><li><p>Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.</p></li><li><p>We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like <code>cons</code>. This allows <code>5 - 1 - 2</code> to be parsed as <code>(- (- 5 1) 2)</code>, but <code>5 :: 1 :: nil</code> to be parsed as <code>(:: 5 (:: 1 nil))</code>.</p></li></ul><p>These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:</p><ul><li><p>We will <strong>not</strong> permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing <code>(+ 1 2)</code> with <code>{1 + 2}</code>.</p></li><li><p>Our implementation will <strong>not</strong> support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.</p></li><li><p>All operators will be binary, and we will <strong>not</strong> support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.</p></li></ul><p>With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"infix.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> + +<span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">{</span><span class="mi">10</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="c1">; =&gt; &#39;(1 7)</span></code></pre><p>Let’s get started.</p><h2><a name="implementing-infix-operators"></a>Implementing infix operators</h2><p>Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:</p><ol><li><p>How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?</p></li><li><p>How can we associate fixity information with certain operators?</p></li></ol><p>We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.</p><p>So, how <em>do</em> we detect if a Racket expression is surrounded by curly braces? Normally, in <code>#lang racket</code>, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated <em>exactly</em> the same as parentheses:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.</p><p>Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29"><em>syntax objects</em></a>. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as <a href="http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29"><em>syntax properties</em></a>. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s <a href="http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29"><em>reader</em></a> (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named <a href="http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29"><code>'paren-shape</code></a>.</p><p>This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="no">#f</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\[</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\{</span></code></pre><p>This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.</p><h3><a name="customizing-application"></a>Customizing application</h3><p>Racket is a language <em>designed</em> to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named <a href="http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29"><code>#%app</code></a>, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>…into this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>What’s special about <code>#%app</code> is that the macroexpander will use whichever <code>#%app</code> is in scope in the expression’s lexical context, so if we write our own version of <code>#%app</code>, it will be used instead of the one from <code>#lang racket</code>. This is what we will use to hook into ordinary Racket expressions.</p><p>To write our custom version of <code>#%app</code>, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the <code>'paren-shape</code> syntax property, <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29"><code>syntax/parse/class/paren-shape</code></a>. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>This code will transform any applications surrounded in curly braces into one that starts with <code>#%infix</code> instead of <code>#%app</code>, so <code>{1 + 2}</code> will become <code>(#%infix 1 + 2)</code>, for example. The identifier <code>#%infix</code> isn’t actually special in any way, it just has a funny name, but we haven’t actually defined <code>#%infix</code> yet, so we need to do that next!</p><p>To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like <code>{1 + 2}</code>, should be converted into the equivalent prefix expressions, in this case <code>(+ 1 2)</code>. We can do this with a simple macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)])</span></code></pre><p>Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the <code>#%app</code> from <code>racket/base</code>, which will avoid any accidental infinite recursion between our <code>#%app</code> and <code>#%infix</code>. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>That’s pretty cool!</p><p>Of course, we probably want to support infix applications with more than just a single binary operator, such as <code>{1 + 2 + 3}</code>. We can implement that just by adding another case to <code>#%infix</code> that handles more subforms:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">10</span></code></pre><p>I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">5</span></code></pre><p>Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as <code>cons</code>, nested <code>-&gt;</code> types or contracts for curried functions, and <code>expt</code>, the exponentiation operator.</p><h3><a name="tracking-operator-fixity"></a>Tracking operator fixity</h3><p>Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.</p><p>Essentially, Racket (like Scheme) uses a <code>define-syntax</code> form to define macros, which is what <code>define-syntax-parser</code> eventually expands into. However, unlike Scheme, Racket’s <code>define-syntax</code> is not <em>just</em> for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Once a binding has been defined using <code>define-syntax</code>, a macro can look up the value associated with it by using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, which returns the compile-time value associated with an identifier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">foo</span><span class="p">)))</span> +<span class="c1">; =&gt; 3</span></code></pre><p>The cool thing is that <code>syntax-local-value</code> gets the value associated with a specific <em>binding</em>, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use <code>syntax-local-value</code> to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the <code>foo</code> binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because <code>foo</code> is not bound to anything at runtime.</p><p>To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the <a href="http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29"><code>prop:procedure</code></a> structure type property.</p><p>If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">))))</span></code></pre><p>This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for <code>runtime-binding</code>, but we can also include a value of our choosing for <code>fixity</code>. Here’s an example:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="nb">cons</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="p">))</span></code></pre><p>This new <code>::</code> binding will act, in every way, just like <code>cons</code>. If we use it in the REPL, you can see that it acts exactly the same:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></code></pre><p>However, we can also use <code>syntax-local-value</code> to extract this binding’s fixity at compile-time, and that’s what makes it interesting:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">::</span><span class="p">))))</span> +<span class="c1">; =&gt; &#39;right</span></code></pre><p>Using this extra compile-time information, we can adjust our <code>#%infix</code> macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used <code>syntax/parse/class/paren-shape</code> to make decisions based on the <code>'paren-shape</code> syntax property, we can use <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29"><code>syntax/parse/class/local-value</code></a> to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span></code></pre><p>Now, we can update <code>#%infix</code> to use our new <code>infix-op</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span></code></pre><p>Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via <code>#:when</code> clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of <code>#%infix</code>, we can successfully use <code>::</code> in an infix expression, and it will be parsed with the associativity that we expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Exciting!</p><h3><a name="a-nicer-interface-for-defining-infix-operators"></a>A nicer interface for defining infix operators</h3><p>We currently have to define infix operators by explicitly using <code>define-syntax</code>, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the <code>define-infix-operator</code> form from the example at the very beginning of this blog post.</p><p>Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the <code>define-syntax</code> definitions we were already writing:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span></code></pre><p>With this in hand, we can define some infix operators with a much nicer syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary <code>#lang racket</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">-1</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">256</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">64</span></code></pre><p>And you know what’s most amazing about this? The entire thing is <strong>only 50 lines of code</strong>. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/local-value</span> +<span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span> +<span class="w"> </span><span class="n">syntax/transformer</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>Racket is a hell of a programming language.</p><h2><a name="applications-limitations-and-implications"></a>Applications, limitations, and implications</h2><p>This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am <em>almost certain</em> that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even <em>locally</em>.</p><p>What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as <code>let-syntax</code> and <code>syntax-parameterize</code>. Using these tools, it would be entirely possible to implement a <code>with-fixity</code> macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make <code>/</code> right associative within a block of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="m">1/6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="nb">/</span><span class="w"> </span><span class="n">right</span><span class="p">])</span> +<span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span> +<span class="mi">1</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of <code>splicing-let</code> and <code>splicing-let-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span> +<span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="n">op:id</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}}]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">op-tmp</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">op</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let</span><span class="w"> </span><span class="p">([</span><span class="n">op-tmp</span><span class="w"> </span><span class="n">op</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">op-tmp</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span></code></pre><p>This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations <em>between</em> operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.</p><p>As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in <a href="https://github.com/lexi-lambda/hackett">Hackett</a> to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow <em>any</em> expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.</p><p>If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt">this file for the definition of infix bindings</a>, as well as <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101">this file for the defintion of infix application</a>. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like <code>{_ - 1}</code> to serve as a shorthand for <code>(lambda [x] {x - 1})</code>, but I haven’t yet decided if I like the tradeoffs involved.</p><p>It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in <code>#lang racket</code> without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.</p><p>Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to <code>#lang hackett</code>, there’s no reason something similar but more powerful couldn’t be built as a separate library by a <em>user</em> of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.</p><ol class="footnotes"></ol></article>Unit testing effectful Haskell with monad-mock2017-06-29T00:00:00Z2017-06-29T00:00:00ZAlexis King<article><p>Nearly eight months ago, <a href="/blog/2016/10/03/using-types-to-unit-test-in-haskell/">I wrote a blog post about unit testing effectful Haskell code</a> using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, <a href="https://hackage.haskell.org/package/monad-mock">monad-mock</a>.</p><h2><a name="a-first-glance-at-monad-mock"></a>A first glance at monad-mock</h2><p>The monad-mock library is, first and foremost, designed to be <em>easy</em>. It doesn’t ask much from you, and it requires almost zero boilerplate.</p><p>The first step is to write an mtl-style interface that encodes an effect you want to mock. For example, you might want to test some code that interacts with the filesystem:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Now you just have to write your code as normal. For demonstration purposes, here’s a function that defines copying a file in terms of <code>readFile</code> and <code>writeFile</code>:</p><pre><code class="pygments"><span class="nf">copyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>Making this function work on the real filesystem is trivial, since we just need to define an instance of <code>MonadFileSystem</code> for <code>IO</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span></code></pre><p>But how do we test this? Well, we <em>could</em> run some real code in <code>IO</code>, which might not be so bad for such a simple function, but this seems like a bad idea. For one thing, a bad implementation of <code>copyFile</code> could do some pretty horrible things if it misbehaved and decided to overwrite important files, and if you’re constantly running a test suite whenever a file changes, it’s easy to imagine causing a lot of damage. Running tests against the real filesystem also makes tests slower and harder to parallelize, and it only gets much worse once you are doing more complex effects than interacting with the filesystem.</p><p>Using monad-mock, we can test this function in just a couple of lines of code:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Exception</span><span class="w"> </span><span class="p">(</span><span class="nf">evaluate</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock.TH</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Data.Function</span><span class="w"> </span><span class="p">((</span><span class="o">&amp;</span><span class="p">))</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Test.Hspec</span> + +<span class="nf">makeMock</span><span class="w"> </span><span class="s">"FileSystemAction"</span><span class="w"> </span><span class="p">[</span><span class="n">ts</span><span class="o">|</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="o">|</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"copyFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reads a file and writes its contents to another file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span></code></pre><p>That’s it!</p><p>The last two lines of the above snippet are the real interesting bits, which specify the actions that are expected to be executed, and it couples them with their results. You will find that if you tweak the list in any way, such as reordering the actions, eliminating one or both of them, or adding an additional action to the end, the test will fail. We could even turn this into a property-based test that generated arbitrary file paths and file contents.</p><p>Admittedly, in this trivial example, the mock is a little silly, since converting this into a property-based test would demonstrate how much we’ve basically just reimplemented the function in our test. However, once our function starts to do somewhat more complicated things, then our tests become more meaningful. Here’s a similar function that only copies a file if it is nonempty:</p><pre><code class="pygments"><span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This function has some logic which is very clearly <em>not</em> expressed in its type, and it would be difficult to encode that information into the type in a safe way. Fortunately, we can guarantee that it works by writing some tests:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">""</span><span class="w"> </span><span class="p">]</span></code></pre><p>These tests are much more useful, and they have some actual value to them. Imagine we had accidentally written <code>when</code> instead of <code>unless</code>, an easy typo to make. Our tests would fail with some useful error messages:</p><pre><code>1) copyNonemptyFile copies a file with contents + uncaught exception: runMockT: expected the following unexecuted actions to be run: + WriteFile "bar.txt" "contents" + +2) copyNonemptyFile does nothing with an empty file + uncaught exception: runMockT: expected end of program, called writeFile + given action: WriteFile "bar.txt" "" +</code></pre><p>You now know enough to write tests with monad-mock.</p><h2><a name="why-unit-test"></a>Why unit test?</h2><p>When the issue of testing is brought up in Haskell, it is often treated with a certain distaste by a portion of the community. There are some points I’ve seen a number of times, and though they take different forms, they boil down to two ideas:</p><ol><li><p>“Haskell code does not need tests because the type system can prove correctness.”</p></li><li><p>“Testing in Haskell is trivial because it is a pure language, and testing pure functions is easy.”</p></li></ol><p>I’ve been writing Haskell professionally for over a year now, and I can happily say that there <em>is</em> some truth to both of those things! When my Haskell code typechecks, I feel a confidence in it that I would not feel were I using a language with a less powerful type system. Furthermore, Haskell encourages a “pure core, impure shell” approach to system design that makes testing many things pleasant and straightforward, and it completely eliminates the worry of subtle nondeterminism leaking into tests.</p><p>That said, Haskell is not a proof assistant, and its type system cannot guarantee everything, especially for code that operates on the boundaries of what Haskell can control. For much the same reason, I find that my pure code is the code I am <em>least</em> likely to need to test, since it is also the code with the strongest type safety guarantees, operating on types in my application’s domain. In contrast, the effectful code is often what I find the most value in extensively testing, since it often contains the most subtle complexity, and it is frequently difficult or even impossible to encode into types.</p><p>Haskell has the power to provide remarkably strong correctness guarantees with a surprisingly small amount of effort by using a combination of tests and types, using each to accommodate for the other’s weaknesses and playing to each technique’s strengths. Some code is test-driven, other code is type-driven. Most code ends up being a mix of both. Testing is just a tool like any other, and it’s nice to feel confident in one’s ability to effectively structure code in a decoupled, testable manner.</p><h2><a name="why-mock"></a>Why mock?</h2><p>Even if you accept that testing is good, the question of whether or not to <em>mock</em> is a subtler issue. To some people, “unit testing” is synonymous with mocks. This is emphatically not true, and in fact, overly aggressive mocking is one of the best ways to make your test suite completely worthless. The monad-mock approach to mocking is a bit more principled than mocking in many dynamic, object-oriented languages, but it comes with many of the same drawbacks: mocks couple your tests to your implementation in ways that make them less valuable and less meaningful.</p><p>For the <code>MonadFileSystem</code> example above, I would actually probably <em>not</em> use a mock. Instead, I would use a <strong>fake</strong>, in-memory filesystem implementation:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)])</span> +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">second</span><span class="w"> </span><span class="n">sort</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">runStateT</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">fs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="o">&amp;</span> +<span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"readFile: no such file ‘"</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"’"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">filter</span><span class="w"> </span><span class="p">((</span><span class="o">/=</span><span class="w"> </span><span class="n">path</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fst</span><span class="p">)</span><span class="w"> </span><span class="n">fs</span></code></pre><p>The above snippet demonstrates how easy it is to define a <code>MonadFileSystem</code> implementation in terms of <code>StateT</code>, and while this may seem like a lot of boilerplate, it really isn’t. You have to write a fake <em>once</em> per interface, and the above block is a minuscule twelve lines of code. With this technique, you are still able to write tests that depend on the state of the filesystem before and after running the implementation, but you decouple yourself from the precise process of getting there:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"bar.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">),</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is better than using a mock, and I would highly recommend doing it if you can! However, a lot of real applications have to interact with services of much greater complexity than an idealized filesystem, and creating that sort of in-memory fake is not always practical. One such situation might be interacting with AWS CloudFormation, for example:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">createStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackName</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StackTemplate</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackId</span><span class="p">)</span> +<span class="w"> </span><span class="n">listStacks</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="p">[</span><span class="kt">StackSummaries</span><span class="p">])</span> +<span class="w"> </span><span class="n">describeStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackId</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackInfo</span><span class="p">)</span> +<span class="w"> </span><span class="c1">-- and so on...</span></code></pre><p>AWS is a very complex system, and it can do dozens of different things (and fail in dozens of different ways) based on an equally complex set of inputs. For example, in the above API, <code>createStack</code> needs to parse its template, which can be YAML or JSON, in order to determine which of many possible errors and behaviors can be produced, both on the initial call and on subsequent ones.</p><p>Creating a fake implementation of <em>AWS</em> is hardly feasible, and this is where a mock can be useful. By simply writing <code>makeMock "AWSAction" [ts| MonadAWS |]</code>, we can test functions that interact with AWS in a pure way without necessarily needing to replicate all of its complexity.</p><h3><a name="isolating-mocks"></a>Isolating mocks</h3><p>Of course, tests that use mocks provide less value than tests that use “smarter” fakes, since they are far more tightly coupled to the implementation, and it’s dramatically more likely that you will need to change the tests when you change the logic. To avoid this, it can be helpful to create multiple interfaces to the same thing: a high-level interface and a low-level one. If our above <code>MonadAWS</code> is a low-level interface, we could create a high-level counterpart that does precisely what our application needs:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDeploy</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">executeDeployment</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>When running our application “for real”, we would use <code>MonadAWS</code> to implement <code>MonadDeploy</code>:</p><pre><code class="pygments"><span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>The nice thing about this is we can actually test <code>executeDeploymentImpl</code> using a <code>MonadAWS</code> mock, so we can still have unit test coverage of the code on the boundaries of our system! Additionally, by containing the mock to a single place, we can test the rest of our code using a smarter fake implementation of <code>MonadDeploy</code>, helping to decouple our code from AWS’s complex API and improve the reliability and usefulness of our test suite.</p><p>They key point here is that mocking is just a small piece of the larger testing puzzle in <em>any</em> language, and that is just as true in Haskell. An overemphasis on mocking is an easy way to end up with a test suite that feels useless, probably because it is. Use mocks as a technique to insulate your application from the complexity in others’ APIs, then use more domain-specific testing techniques and type-level assertions to ensure the correctness of your logic.</p><h2><a name="how-monad-mock-works"></a>How monad-mock works</h2><p>If you’ve read this far and are convinced that monad-mock is useful, you may safely stop reading now. However, if you are interested in the details of what it actually does and what makes it tick, the rest of this blog post is going to focus on how the implementation works and how it compares to other techniques.</p><p>The centerpiece of monad-mock’s API is its monad transformer, <code>MockT</code>, which is a type constructor that accepts three types:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="p">)</span></code></pre><p>The <code>m</code> and <code>a</code> type variables obviously correspond to the usual monad transformer arguments, which represent the underlying monad and the result of the monadic computation, respectively. The <code>f</code> variable is more interesting, since it’s what makes <code>MockT</code> work at all, and it isn’t even a type: it’s a type constructor with kind <code>* -&gt; *</code>. What does it mean?</p><p>Looking at the type signature of <code>runMockT</code> gives us a little bit more information about what that <code>f</code> actually represents:</p><pre><code class="pygments"><span class="nf">runMockT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>This type signature provides two pieces of key information:</p><ol><li><p>The <code>f</code> parameter is constrained by the <code>Action f</code> constraint.</p></li><li><p>Running a mocked computation requires supplying a list of <code>WithResult f</code> values. This list corresponds to the list of expectations provided to <code>runMock</code> in earlier examples.</p></li></ol><p>To understand both of these things, it helps to examine the definition of an actual datatype that can have an <code>Action</code> instance. For the filesystem example, the action datatype looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Notice how each constructor clearly corresponds to one of the methods of <code>MonadFileSystem</code>, with a type to match. Now the purpose of the type provided to the <code>FileSystemAction</code> constructor (in this case <code>r</code>) should hopefully become clear: it represents the type of the value <em>produced</em> by each method. Also note that the type is completely phantom—it does not appear in negative position in any of the constructors.</p><p>With this in mind, we can take a look at the definition of <code>WithResult</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="p">(</span><span class="kt">:-&gt;</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span></code></pre><p>This is what defines the <code>(:-&gt;)</code> constructor from earlier in the blog post, and you can see that it effectively just represents a tuple of an action and a value of its associated result. It’s completely type-safe, since it ensures the result matches the type argument to the action.</p><p>Finally, this brings us to the <code>Action</code> class, which is not complex, but is unfortunately necessary:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">eqAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="n">showAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Notice that these methods are effectively just <code>(==)</code> and <code>show</code>, lifted to type constructors of kind <code>* -&gt; *</code>. One significant difference is that <code>eqAction</code> produces <code>Maybe (a :~: b)</code> instead of <code>Bool</code>, where <code>(:~:)</code> is from <code>Data.Type.Equality</code>. This is a type equality witness, which means a successful equality between two values allows the compiler to be sure that the two <em>types</em> are equal. This is necessary for the implementation of <code>runMockT</code> due to the phantom type in actions—in order to convince GHC that we can properly return the result of a mocked action, we need to assure it that the value we’re going to return is actually of the proper type.</p><p>Implementing this typeclass is not particularly burdensome, but it’s entirely boilerplate, so even if you want to define your own action type (that is, you don’t want to use <code>makeMock</code>), you can use the <code>deriveAction</code> function from <code>Control.Monad.Mock.TH</code> to derive an <code>Action</code> instance on an existing datatype.</p><h3><a name="connecting-the-mock-to-its-class"></a>Connecting the mock to its class</h3><p>Now that we have an action with which to mock a class, we need to actually define an instance of that class for <code>MockT</code>. For this process, monad-mock provides a <code>mockAction</code> function with the following type:</p><pre><code class="pygments"><span class="nf">mockAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span></code></pre><p>This function accepts two arguments: the name of the method being mocked and the action that represents the current call. This is easier to illustrate with an actual instance of <code>MonadFileSystem</code> using <code>MockT</code> and our <code>FileSystemAction</code> type:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MockT</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"readFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"writeFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>This allows <code>readFile</code> and <code>writeFile</code> to defer to the mock, and providing the names of the functions as strings helps monad-mock to produce useful error messages upon failure. Internally, <code>MockT</code> is a <code>StateT</code> that keeps track of a list of <code>WithResult f</code> values as its state. Each call to the mock checks the action against the internal list of calls, and if they match, it returns the associated result. Otherwise, it throws an exception.</p><p>This scheme is simple, but it seems to work remarkably well. There are some obvious enhancements that will probably be eventually necessary, like allowing action results that run in the underlying monad <code>m</code> in order to support things like <code>throwError</code> from <code>MonadError</code>, but so far, it hasn’t been necessary for what we’ve been using it for. Certain tricky signatures defy this simple technique, such as signatures where a monadic action appears in a negative position (that is, the signatures you need things like <a href="https://hackage.haskell.org/package/monad-control">monad-control</a> or <a href="https://hackage.haskell.org/package/monad-unlift">monad-unlift</a> for), but we’ve found that most of our effects don’t have any reason to include such signatures.</p><h2><a name="a-brief-comparison-with-free-r-monads"></a>A brief comparison with free(r) monads</h2><p>At this point, astute readers will likely be thinking about free monads, which parts of this technique greatly resemble. The representation of actions as GADTs is especially similar to <a href="https://hackage.haskell.org/package/freer">freer</a>, which does something extremely similar. Indeed, you can think of this technique as something that combines a freer-style representation with mtl-style classes. Given that freer already does this, you might ask yourself what the point is.</p><p>If you are already sold on free monads, monad-mock may very well be uninteresting to you. From the perspective of theoretical novelty, monad-mock is not anything new or different. However, there are a variety of practical reasons to prefer mtl over free, and it’s nice to see how easy it is to enjoy the testing benefits of free without too much extra effort.</p><p>An in-depth comparison between mtl and free is well outside the scope of this blog post. However, the key point is that this technique <em>only</em> affects test code, so the real runtime implementation will not be affected in any way. This means you can take advantage of the performance benefits and ecosystem support of mtl without sacrificing simple, expressive testing.</p><h2><a name="conclusion"></a>Conclusion</h2><p>To cap things off, I want to emphasize monad-mock’s role as a single part of a larger initiative we’ve been making for the better part of the past eighteen months. Haskell is a language with ever-evolving techniques and style, and it’s sometimes dizzying to figure out how to use all the pieces together to develop robust, maintainable applications. While monad-mock might not be anything drastically different from existing testing techniques, my hope is that it can provide an opinionated mechanism to make testing easy and accessible, even for complex interactions with other services and systems.</p><p>I’ve made an effort to make it abundantly clear in this blog post that monad-mock is <em>not</em> a silver bullet to testing, and in fact, I would prefer other techniques for ensuring correctness whenever possible. Even so, mocking is a nice tool to have in your toolbox, and it’s a good fallback to get even the worst APIs under test coverage.</p><p>If you want to try out monad-mock for yourself, <a href="https://hackage.haskell.org/package/monad-mock">take a look at the documentation on Hackage</a> and start playing around! It’s still early software, so it’s not the most proven or featureful, but we’ve managed to get mileage out of it already, all the same. If you find any problems, have a use case it does not support, or just find something about it unclear, please do not hesitate to <a href="https://github.com/cjdev/monad-mock">open an issue on the GitHub repository</a>—we obviously can’t fix issues we don’t know about.</p><p>Thanks as always to the many people who have contributed ideas that have shaped my philosophy and approach to testing and have helped provide the tools that make this library work. Happy testing!</p><ol class="footnotes"></ol></article>Realizing Hackett, a metaprogrammable Haskell2017-05-27T00:00:00Z2017-05-27T00:00:00ZAlexis King<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Lifts for free: making mtl typeclasses derivable2017-04-28T00:00:00Z2017-04-28T00:00:00ZAlexis King<article><p>Perhaps the most important abstraction a Haskell programmer must understand to effectively write modern Haskell code, beyond the level of the monad, is the <em>monad transformer</em>, a way to compose monads together in a limited fashion. One frustrating downside to monad transformers is a proliferation of <code>lift</code>s, which explicitly indicate which monad in a transformer “stack” a particular computation should run in. Fortunately, the venerable <a href="https://hackage.haskell.org/package/mtl">mtl</a> provides typeclasses that make this lifting mostly automatic, using typeclass machinery to insert <code>lift</code> where appropriate.</p><p>Less fortunately, the mtl approach does not actually eliminate <code>lift</code> entirely, it simply moves it from use sites to instances. This requires a small zoo of extraordinarily boilerplate-y instances, most of which simply implement each typeclass method using <code>lift</code>. While we cannot eliminate the instances entirely without somewhat dangerous techniques like <a href="https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#overlapping-instances">overlapping instances</a>, we <em>can</em> automatically derive them using features of modern GHC, eliminating the truly unnecessary boilerplate.</p><h2><a name="the-problem-with-mtl-style-typeclasses"></a>The problem with mtl-style typeclasses</h2><p>To understand what problem it is exactly that we’re trying to solve, we first need to take a look at an actual mtl-style typeclass. I am going to start with an mtl-<em>style</em> typeclass, rather than an actual typeclass in the mtl, due to slight complications with mtl’s actual typeclasses that we’ll get into later. Instead, let’s start with a somewhat boring typeclass, which we’ll call <code>MonadExit</code>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">System.Exit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitCode</span><span class="p">)</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is a simple typeclass that abstracts over the concept of early exit, given an exit code. The most obvious implementation of this typeclass is over <code>IO</code>, which will actually exit the program:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="k">qualified</span><span class="w"> </span><span class="nn">System.Exit</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">IO</span><span class="w"> </span><span class="p">(</span><span class="n">exitWith</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IO</span><span class="o">.</span><span class="n">exitWith</span></code></pre><p>One of the cool things about these typeclasses, though, is that we don’t have to have just one implementation. We could also write a pure implementation of <code>MonadExit</code>, which would simply short-circuit the current computation and return the <code>ExitCode</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">ExitCode</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span></code></pre><p>Instead of simply having an instance on a concrete monad, though, we probably want to be able to use this in a larger monad stack, so we can define an <code>ExitT</code> monad transformer that can be inserted into any monad transformer stack:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Except</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="p">,</span><span class="w"> </span><span class="nf">runExceptT</span><span class="p">,</span><span class="w"> </span><span class="nf">throwError</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Trans</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTrans</span><span class="p">)</span> + +<span class="nf">runExitT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">runExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span></code></pre><p>With this in place, we can write actual programs using our <code>ExitT</code> monad transformer:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">runExitT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">putStrLn</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">putStrLn</span><span class="w"> </span><span class="s">"world"</span> +<span class="nf">hello</span> +<span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>This is pretty cool! Unfortunately, experienced readers will see the rather large problem with what we have so far. Specifically, it won’t actually work if we try and wrap <code>ExitT</code> in another monad transformer:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runExitT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="n">runReaderT</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">password</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"password1234"</span><span class="p">)</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="c1">-- super secure password</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"access granted"</span> + +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"not the right password"</span> +<span class="o">&lt;</span><span class="n">interactive</span><span class="o">&gt;:</span><span class="w"> </span><span class="ne">error</span><span class="kt">:</span> +<span class="w"> </span><span class="err">•</span><span class="w"> </span><span class="kt">No</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="n">for</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">[</span><span class="kt">Char</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m0</span><span class="p">)))</span> +<span class="w"> </span><span class="n">arising</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">use</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="err">‘</span><span class="n">it</span><span class="err">’</span> +<span class="w"> </span><span class="err">•</span><span class="w"> </span><span class="kt">In</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">stmt</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">interactive</span><span class="w"> </span><span class="kt">GHCi</span><span class="w"> </span><span class="n">command</span><span class="kt">:</span><span class="w"> </span><span class="n">print</span><span class="w"> </span><span class="n">it</span></code></pre><p>The error message is relatively self-explanatory if you are familiar with mtl error messages: there is no <code>MonadExit</code> instance for <code>ReaderT</code>. This makes sense, since we only defined a <code>MonadExit</code> instance for <em><code>ExitT</code></em>, nothing else. Fortunately, the instance for <code>ReaderT</code> is completely trivial, since we just need to use <code>lift</code> to delegate to the next monad in the stack:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>Now that the delegating instance is set up, we can actually use our <code>logIn</code> function:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"not the right password"</span> +<span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"password1234"</span> +<span class="kt">Right</span><span class="w"> </span><span class="s">"access granted"</span></code></pre><h3><a name="an-embarrassment-of-instances"></a>An embarrassment of instances</h3><p>We’ve managed to make our program work properly now, but we’ve still only defined the delegating instance for <code>ReaderT</code>. What if someone wants to use <code>ExitT</code> with <code>WriterT</code>? Or <code>StateT</code>? Or any of <code>ExceptT</code>, <code>RWST</code>, or <code>ContT</code>? Well, we have to define instances for each and every one of them, and as it turns out, the instances are all identical!</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ContT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>This is bad enough on its own, but this is actually the <em>simplest</em> case: a typeclass with a single method which is trivially lifted through any other monad transformer. Another thing we’ve glossed over is actually defining all the delegating instances for the <em>other</em> mtl typeclasses on <code>ExitT</code> itself. Fortunately, we can derive these ones with <code>GeneralizedNewtypeDeriving</code>, since <code>ExceptT</code> has already done most of the work for us:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="w"> </span><span class="c1">-- base</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="c1">-- transformers-base</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTrans</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="c1">-- mtl</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadThrow</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadCatch</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadMask</span><span class="w"> </span><span class="c1">-- exceptions</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="c1">-- monad-control</span> +<span class="w"> </span><span class="p">)</span></code></pre><p>Unfortunately, we have to write the <code>MonadError</code> instance manually if we want it, since we don’t want to pick up the instance from <code>ExceptT</code>, but rather wish to defer to the underlying monad. This means writing some truly horrid delegation code:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span> + +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x&#39;</span></code></pre><p>(Notably, this is so awful because <code>catchError</code> is more complex than the simple <code>exitWith</code> method we’ve studied so far, which is why we’re starting with a simpler typeclass. We’ll get more into this later, as promised.)</p><p>This huge number of instances is sometimes referred to as the “n<sup>2</sup> instances” problem, since it requires every monad transformer have an instance of every single mtl-style typeclass. Fortunately, in practice, this proliferation is often less horrible than it might seem, mostly because deriving helps a lot. However, remember that if <code>ExitT</code> <em>weren’t</em> a simple wrapper around an existing monad transformer, we wouldn’t be able to derive the instances at all! Instead, we’d have to write them all out by hand, just like we did with all the <code>MonadExit</code> instances.</p><p>It’s a shame that these typeclass instances can’t be derived in a more general way, allowing derivation for arbitrary monad transformers instead of simply requiring the newtype deriving machinery. As it turns out, with clever use of modern GHC features, we actually <strong>can</strong>. It’s not even all that hard.</p><h2><a name="default-instances-with-default-signatures"></a>Default instances with default signatures</h2><p>It’s not hard to see that our <code>MonadExit</code> instances are all exactly the same: just <code>lift . exitWith</code>. Why is that, though? Well, every instance is an instance on a monad transformer over a monad that is already an instance of <code>MonadExit</code>. In fact, we can express this in a type signature, and we can extract <code>lift . exitWith</code> into a separate function:</p><pre><code class="pygments"><span class="nf">defaultExitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">defaultExitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>However, writing <code>defaultExitWith</code> really isn’t any easier than writing <code>lift . exitWith</code>, so this deduplication doesn’t really buy us anything. However, it <em>does</em> indicate that we could write a default implementation of <code>exitWith</code> if we could require just a little bit more from the implementing type. With <a href="https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#default-method-signatures">GHC’s <code>DefaultSignatures</code> extension</a>, we can do precisely that.</p><p>The idea is that we can write a separate type signature for a default implementation of <code>exitWith</code>, which can be more specific than the type signature for <code>exitWith</code> in general. This allows us to use our <code>defaultExitWith</code> implementation more or less directly:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DefaultSignatures #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>We have to use <code>m1</code> instead of <code>m</code>, since type variables in the instance head are always scoped, and the names would conflict. However, this creates another problem, since our specialized type signature replaces <code>m</code> with <code>t m1</code>, which won’t quite work (as GHC can’t automatically figure out they should be the same). Instead, we can use <code>m</code> in the type signature, then just add a type equality constraint ensuring that <code>m</code> and <code>t m1</code> must be the same type:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>Now we can write all of our simple instances without even needing to write a real implementation! All of the instance bodies can be empty:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ContT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>While this doesn’t completely alleviate the pain of writing instances, it’s definitely an improvement over what we had before. With <a href="https://downloads.haskell.org/~ghc/8.2.1-rc1/docs/html/users_guide/glasgow_exts.html#deriving-strategies">GHC 8.2’s new <code>DerivingStrategies</code> extension</a>, it becomes especially beneficial when defining entirely new transformers that should also have <code>ExitT</code> instances, since they can be derived with <code>DeriveAnyClass</code>:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="n">anyclass</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="p">)</span></code></pre><p>This is pretty wonderful.</p><p>Given that only <code>MonadExit</code> supports being derived in this way, we sadly still need to implement the other, more standard mtl-style typeclasses ourselves, like <code>MonadIO</code>, <code>MonadBase</code>, <code>MonadReader</code>, <code>MonadWriter</code>, etc. However, what if all of those classes provided the same convenient default signatures that our <code>MonadExit</code> does? If that were the case, then we could write something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="n">anyclass</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadThrow</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadCatch</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadMask</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span> +<span class="w"> </span><span class="p">)</span></code></pre><p>Compared to having to write all those instances by hand, this would be a pretty enormous difference. Unfortunately, many of these typeclasses are not quite as simple as our <code>MonadExit</code>, and we’d have to be a bit more clever to make them derivable.</p><h2><a name="making-mtl-s-classes-derivable"></a>Making mtl’s classes derivable</h2><p>Our <code>MonadExit</code> class was extremely simple, since it only had a single method with a particularly simple type signature. For reference, this was the type of our generic <code>exitWith</code>:</p><pre><code class="pygments"><span class="nf">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Let’s now turn our attention to <code>MonadReader</code>. At first blush, this typeclass should not be any trickier to implement than <code>MonadExit</code>, since the types of <code>ask</code> and <code>reader</code> are both quite simple:</p><pre><code class="pygments"><span class="nf">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="nf">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>However, the type of the other method, <code>local</code>, throws a bit of a wrench in our plans. It has the following type signature:</p><pre><code class="pygments"><span class="nf">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Why is this so much more complicated? Well, the key is in the second argument, which has the type <code>m a</code>. That’s not something that can be simply <code>lift</code>ed away! Try it yourself: try to write a <code>MonadReader</code> instance for some monad transformer. It’s not as easy as it looks!</p><p>We can illustrate the problem by creating our own version of <code>MonadReader</code> and implementing it for something like <code>ExceptT</code> ourselves. We can start with the trivial methods first:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span></code></pre><p>However, implementing <code>local</code> is harder. Let’s specialize the type signature to <code>ExceptT</code> to make it more clear why:</p><pre><code class="pygments"><span class="nf">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Our base monad, <code>m</code>, implements <code>local</code>, but we have to convert the first argument from <code>ExceptT e m a</code> into <code>m (Either e a)</code> first, run it through <code>local</code> in <code>m</code>, then wrap it back up in <code>ExceptT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>This operation is actually a mapping operation of sorts, since we’re mapping <code>local f</code> over <code>x</code>. For that reason, this can be rewritten using the <code>mapExceptT</code> function provided from <code>Control.Monad.Except</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapExceptT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">local</span></code></pre><p>If you implement <code>MonadReader</code> instances for other transformers, like <code>StateT</code> and <code>WriterT</code>, you’ll find that the instances are exactly the same <em>except</em> for <code>mapExceptT</code>, which is replaced with <code>mapStateT</code> and <code>mapWriterT</code>, respectively. This is sort of obnoxious, given that we want to figure out how to create a generic version of <code>local</code> that works with any monad transformer, but this requires concrete information about which monad we’re in. Obviously, the power <code>MonadTrans</code> gives us is not enough to make this generic. Fortunately, there is a typeclass which does: <a href="http://hackage.haskell.org/package/monad-control-1.0.1.0/docs/Control-Monad-Trans-Control.html#t:MonadTransControl"><code>MonadTransControl</code></a> from the <code>monad-control</code> package.</p><p>Using <code>MonadTransControl</code>, we can write a generic <code>mapT</code> function that maps over an arbitrary monad transformer with a <code>MonadTransControl</code> instance:</p><pre><code class="pygments"><span class="nf">mapT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="p">),</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span> +<span class="nf">mapT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">return</span></code></pre><p>This type signature may look complicated (and, well, it is), but the idea is that the <code>StT</code> associated type family encapsulates the monadic state that <code>t</code> introduces. For example, for <code>ExceptT</code>, <code>StT (ExceptT e) a</code> is <code>Either e a</code>. For <code>StateT</code>, <code>StT (StateT s) a</code> is <code>(a, s)</code>. Some transformers, like <code>ReaderT</code>, have no state, so <code>StT (ReaderT r) a</code> is just <code>a</code>.</p><p>I will not go into the precise mechanics of how <code>MonadTransControl</code> works in this blog post, but it doesn’t matter significantly; the point is that we can now use <code>mapT</code> to create a generic implementation of <code>local</code> for use with <code>DefaultSignatures</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> + +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">local</span> + +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">ask</span></code></pre><p>Once more, we now get instances of our typeclass, in this case <code>MonadReader</code>, <strong>for free</strong>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>It’s also worth noting that we <em>don’t</em> get a <code>ContT</code> instance for free, even though <code>ContT</code> has a <code>MonadReader</code> instance in mtl. Unlike the other monad transformers mtl provides, <code>ContT</code> does not have a <code>MonadTransControl</code> instance because it cannot be generally mapped over. While a <code>mapContT</code> function does exist, its signature is more restricted:</p><pre><code class="pygments"><span class="nf">mapContT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ContT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ContT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>It happens that <code>local</code> can still be implemented for <code>ContT</code>, so it can still have a <code>MonadReader</code> instance, but it cannot be derived in the same way as it can for the other transformers. Still, in practice, I’ve found that most user-defined transformers do not have such complex control flow, so they can safely be instances of <code>MonadTransControl</code>, and they get this deriving for free.</p><h3><a name="extending-this-technique-to-other-mtl-typeclasses"></a>Extending this technique to other mtl typeclasses</h3><p>The default instances for the other mtl typeclasses are slightly different from the one for <code>MonadReader</code>, but for the most part, the same general technique applies. Here’s a derivable <code>MonadError</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span> + +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">f</span><span class="p">))</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">return</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>The <code>MonadState</code> interface turns out to be extremely simple, so it doesn’t even need <code>MonadTransControl</code> at all:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">get</span> + +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">put</span> + +<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>Everything seems to be going well! However, not everything is quite so simple.</p><h3><a name="a-monadwriter-diversion"></a>A <code>MonadWriter</code> diversion</h3><p>Unexpectedly, <code>MonadWriter</code> turns out to be by far the trickiest of the bunch. It’s not too hard to create default implementations for most of the methods of the typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="p">(</span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">writer</span> + +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tell</span> + +<span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="n">y&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="p">(</span><span class="n">return</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">y&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span></code></pre><p>However, <code>MonadWriter</code> has a fourth method, <code>pass</code>, which has a particularly tricky type signature:</p><pre><code class="pygments"><span class="nf">pass</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>As far as I can tell, this is not possible to generalize using <code>MonadTransControl</code> alone, since it would require inspection of the result of the monadic argument (that is, it would require a function from <code>StT t (a, b) -&gt; (StT t a, b)</code>), which is not possible in general. My gut is that this could likely also be generalized with a slightly more powerful abstraction than <code>MonadTransControl</code>, but it is not immediately obvious to me what that abstraction should be.</p><p>One extremely simple way to make this possible would be to design something to serve this specific use case:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">RunSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="o">.</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="kt">RunSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Instances of <code>MonadTransSplit</code> would basically just provide a way to pull out bits of the result, if possible:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">Nothing</span><span class="p">)</span> +<span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">),</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">),</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span></code></pre><p>Then, using this, it would be possible to write a generic version of <code>pass</code>:</p><pre><code class="pygments"><span class="kr">default</span><span class="w"> </span><span class="n">pass</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">pass</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pass</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="kr">case</span> +<span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">f</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Nothing</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="p">(</span><span class="n">return</span><span class="w"> </span><span class="n">r</span><span class="p">)</span></code></pre><p>However, this seems pretty overkill for just one particular method, given that I have no idea if <code>MonadTransSplit</code> would be useful <em>anywhere</em> else. One interesting thing about going down this rabbit hole, though, is that I learned that <code>pass</code> has some somewhat surprising behavior when mixed with transformers like <code>ExceptT</code> or <code>MaybeT</code>, if you don’t carefully consider how it works. It’s a strange method with a somewhat strange interface, so I don’t think I have a satisfactory conclusion about <code>MonadWriter</code> yet.</p><h2><a name="regrouping-and-stepping-back"></a>Regrouping and stepping back</h2><p>Alright, that was a lot of fairly intense, potentially confusing code. What the heck did we actually accomplish? Well, we got a couple of things:</p><ol><li><p>First, we developed a technique for writing simple mtl-style typeclasses that are derivable using <code>DeriveAnyClass</code> (or simply writing an empty instance declaration). We used a <code>MonadExit</code> class as a proof of concept, but really, the technique is applicable to most mtl-style typeclasses that represent simple effects (including, for example, <code>MonadIO</code>).</p><p>This technique is useful in isolation, even if you completely disregard the rest of the blog post. For an example where I recently applied it in real code, see <a href="https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L226-L271">the default signatures provided with <code>MonadPersist</code> from the <code>monad-persist</code> library</a>, which make <a href="https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L506-L513">defining instances completely trivial</a>. If you use mtl-style typeclasses in your own application to model effects, I don’t see much of a reason <em>not</em> to use this technique.</p></li><li><p>After <code>MonadExit</code>, we applied the same technique to the mtl-provided typeclasses <code>MonadReader</code>, <code>MonadError</code>, and <code>MonadState</code>. These are a bit trickier, since the first two need <code>MonadTransControl</code> in addition to the usual <code>MonadTrans</code>.</p><p>Whether or not this sort of thing should actually be added to mtl itself probably remains to be seen. For the simplest typeclass, <code>MonadState</code>, it seems like there probably aren’t many downsides, but given the difficulty implementing it for <code>MonadWriter</code> (or, heaven forbid, <code>MonadCont</code>, which I didn’t even seriously take a look at for this blog post), it doesn’t seem like an obvious win. Consistency is important.</p><p>Another downside that I sort of glossed over is possibly even more significant from a practical point of view: adding default signatures to <code>MonadReader</code> would require the removal of the default implementation of <code>ask</code> that is provided by the existing library (which implements <code>ask</code> in terms of <code>reader</code>). This would be backwards-incompatible, so it’d be difficult to change, even if people wanted to do it. Still, it’s interesting to consider what these typeclasses might look like if they were designed today.</p></li></ol><p>Overall, these techniques are not a silver bullet for deriving mtl-style typeclasses, nor do they eliminate the n<sup>2</sup> instances problem that mtl style suffers from. That said, they <em>do</em> significantly reduce boilerplate and clutter in the simplest cases, and they demonstrate how modern Haskell’s hierarchy of typeclasses provides a lot of power, both to describe quite abstract concepts and to alleviate the need to write code by hand.</p><p>I will continue to experiment with the ideas described in this blog post, and I’m sure some more pros and cons will surface as I explore the design space. If you have any suggestions for how to deal with “the <code>MonadWriter</code> problem”, I’d be very interested to hear them! In the meantime, consider using the technique in your application code when writing effectful, monadic typeclasses.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questions2017-01-05T00:00:00Z2017-01-05T00:00:00ZAlexis King<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheses2017-01-02T00:00:00Z2017-01-02T00:00:00ZAlexis King<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Using types to unit-test in Haskell2016-10-03T00:00:00Z2016-10-03T00:00:00ZAlexis King<article><p>Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.</p><p>Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.</p><h2><a name="first-an-aside-on-testing-philosophy"></a>First, an aside on testing philosophy</h2><p>Testing methodology is a controversial topic within the larger programming community, and there are a multitude of different approaches. This blog post is about <em>unit testing</em>, an already nebulous term with a number of different definitions. For the purposes of this post, I will define a unit test as a test that stubs out collaborators of the code under test in some way. Accomplishing that in Haskell is what this is primarily about.</p><p>I want to be clear that I do not think that unit tests are the only way to write tests, nor the best way, nor even always an applicable way. Depending on your domain, rigorous unit testing might not even make sense, and other forms of tests (end-to-end, integration, benchmarks, etc.) might fulfill your needs.</p><p>In practice, though, implementing those other kinds of tests seems to be well-documented in Haskell compared to pure, object-oriented style unit testing. As my Haskell applications have grown, I have found myself wanting a more fine-grained testing tool that allows me to both test a piece of my codebase in isolation and also use my domain-specific types. This blog post is about that.</p><p>With that disclaimer out of the way, let’s talk about testing in Haskell.</p><h2><a name="drawing-seams-using-types"></a>Drawing seams using types</h2><p>One of the primary attributes of unit tests in object-oriented languages, especially statically-typed ones, is the concept of “seams” within a codebase. These are internal boundaries between components of a system. Some boundaries are obvious—interactions with a database, manipulation of the file system, and performing I/O over the network, to name a few examples—but others are more subtle. Especially in larger codebases, it can be helpful to isolate two related but distinct pieces of functionality as much as possible, which makes them easier to reason about, even if they’re actually part of the same codebase.</p><p>In OO languages, these seams are often marked using interfaces, whether explicitly (in the case of static languages) or implicitly (in the case of dynamic ones). By programming to an interface, it’s possible to create “fake” implementations of that interface for use in unit tests, effectively making it possible to stub out code that isn’t directly relevant to the code being tested.</p><p>In Haskell, representing these seams is a lot less obvious. Consider a fairly trivial function that reverses a file’s contents on the file system:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span></code></pre><p>This function is impossible to test without testing against a real file system. It simply performs I/O directly, and there’s no way to “mock out” the file system for testing purposes. Now, admittedly, this function is so trivial that a unit test might not seem worth the cost, but consider a slightly more complicated function that interacts with a database:</p><pre><code class="pygments"><span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>It might now be a bit more clear that it could be useful to test the above function without running a real database and doing all the necessary context setup before each test case. Indeed, it would be nice if a test could just provide stubbed implementations for <code>fetchUser</code> and <code>fetchRecentPosts</code>, then make assertions about the output.</p><p>One way to solve this problem is to pass the results of those two functions to <code>renderUserProfile</code> as arguments, turning it into a pure function that could be easily tested. This becomes obnoxious for functions of even just slightly more complexity, though (it is not unreasonable to imagine needing a handful of different queries to render a user’s profile page), and it requires significantly restructuring code simply because the tests need it.</p><p>The above code is not only difficult to test, however—it has another problem, too. Specifically, both functions return <code>IO</code> values, which means they can effectively do <em>anything</em>. Haskell has a very strong type system for typing terms, but it doesn’t provide any guarantees about effects beyond a simple yes/no answer about function purity. Even though the <code>renderUserProfile</code> function should really only need to interact with the database, it could theoretically delete files, send emails, make HTTP requests, or do any number of other things.</p><p>Fortunately, it’s possible to solve <em>both</em> problems—a lack of testability and a lack of type safety—using the same general technique. This approach is reminiscent of the interface-based seams of object-oriented languages, but unlike most object-oriented approaches, it provides additional type safety guarantees without the need to explicitly modify the code to support some kind of dependency injection.</p><h3><a name="making-implicit-interfaces-explicit"></a>Making implicit interfaces explicit</h3><p>Statically typed, object-oriented languages provide interfaces as a language construct to encode certain kinds of contracts into the type system, and Haskell has something similar. Typeclasses are, in many ways, an analog to OO interfaces, and they can be used in a similar way. In the above case, let’s write down interfaces that the <code>reverseFile</code> and <code>renderUserProfile</code> functions can use:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span></code></pre><p>The really nice thing about these interfaces is that our function implementations don’t have to change <em>at all</em> to take advantage of them. In fact, all we have to change is their types:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span> + +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty neat, since we haven’t had to alter our code at all, but we’ve managed to completely decouple ourselves from <code>IO</code>. This has the direct effect of both making our code more abstract (we no longer rely on the “real” file system or a “real” database, which makes our code easier to test) and restricting what our functions can do (just from looking at the type signatures, we know what side-effects they can perform).</p><p>Of course, since we’re now coding against an interface, our code doesn’t actually do much of anything. If we want to actually use the functions we’ve written, we’ll have to define instances of <code>MonadFS</code> and <code>MonadDB</code>. When actually running our code, we’ll probably still use <code>IO</code> (or some monad transformer stack with <code>IO</code> at the bottom), so we can define trivial instances for that existing use case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchUser</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchRecentPosts</span></code></pre><p>Even if we go no further, <strong>this is already incredibly useful</strong>. By restricting the sorts of effects our functions can perform at the type level, it becomes a lot easier to see which code is interacting with what. This can be invaluable when working in a part of a moderately large codebase that you are unfamiliar with. Even if the only instance of these typeclasses is <code>IO</code>, the benefits are immediately apparent.</p><p>Of course, this blog post is about testing, so we’re going to go further and take advantage of these seams we’ve now drawn. The question is: how?</p><h2><a name="testing-with-typeclasses-an-initial-attempt"></a>Testing with typeclasses: an initial attempt</h2><p>Given that we now have functions depending on an interface instead of <code>IO</code>, we can create separate instances of our typeclasses for use in tests. Let’s start with the <code>renderUserProfile</code> function. We’ll create a simple wrapper around the <code>Identity</code> type, since we don’t actually care much about the “effects” of our <code>MonadDB</code> methods:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Functor.Identity</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">unTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now, we’ll create a trivial instance of <code>MonadDB</code> for <code>TestM</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa"</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">Post</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">postTitle</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span></code></pre><p>With this instance, it’s now possible to write a simple unit test of the <code>renderUserProfile</code> function that doesn’t need a real database running at all:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"renderUserProfile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows the user’s name"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="s">"Alyssa’s Profile"</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows a list of the user’s posts"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">li</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty nice, and running the above tests reveals a nice property of these kinds of isolated test cases: the test suite runs <em>really, really fast</em>. Communicating with a database, even in extremely simple ways, takes a measurable amount of time, especially with dozens of tests. In contrast, even with hundreds of tests, our unit test suite runs in less than a tenth of a second.</p><p>This all seems to be successful, so let’s try and apply the same testing technique to <code>reverseFile</code>.</p><h3><a name="testing-side-effectful-code"></a>Testing side-effectful code</h3><p>Looking at the type signature for <code>reverseFile</code>, we have a small problem:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Specifically, the return type is <code>()</code>. Making any assertions against the result of this function would be completely worthless, given that it’s guaranteed to be the same exact thing each time. Instead, <code>reverseFile</code> is inherently side-effectful, so we want to be able to test that it properly interacts with the file system in the correct way.</p><p>In order to do this, a simple wrapper around <code>Identity</code> won’t be enough, but we can replace it with something more powerful: <code>Writer</code>. Specifically, we can use a writer monad to “log” what gets called in order to test side-effects. We’ll start by creating a new <code>TestM</code> type, just like last time:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">])</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span></code></pre><p>Using this slightly more powerful type, we can write a useful instance of <code>MonadFS</code> that will track the argument given to <code>writeFile</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span></code></pre><p>Again, the instance is quite simple, but it now enables us to write a straightforward unit test for <code>reverseFile</code>:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span></code></pre><p>Again, quite simple to both implement and use, and the test itself is blindingly fast. There’s another problem, though, which is that we have technically left part of <code>reverseFile</code> untested: we’ve completely ignored the <code>path</code> argument.</p><p>In this contrived example, it may seem silly to test something so trivial, but in real code, it’s quite possible that one would care very much about testing multiple different aspects about a single function. When testing <code>renderUserProfile</code>, this was not hard, since we could reuse the same <code>TestM</code> type and <code>MonadDB</code> instance for both test cases, but in the <code>reverseFile</code> example, we’ve ignored the path entirely.</p><p>We <em>could</em> adjust our <code>MonadFS</code> instance to also track the path provided to each method, but this has a few problems. First, it means every test case would depend on all the various properties we are testing, which would mean updating every test case when we add a new one. It would also be simply impossible if we needed to track multiple types—in this particular case, it turns out that <code>String</code> and <code>FilePath</code> are actually the same type, but in practice, there may be a handful of disparate, incompatible types.</p><p>Both of the above issues could be fixed by creating a sum type and manually filtering out the relevant elements in each test case, but a much more intuitive approach would be to simply have a separate instance for each case. Unfortunately, in Haskell, creating a new instance means creating an entirely new type. To illustrate how much duplication that would entail, we could create the following type and instance for testing proper propagation of the <code>path</code> argument:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">])</span> + +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span></code></pre><p>Now it’s possible to add an extra test case that asserts that the proper path is provided to the two filesystem functions:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This works, but it’s ultimately unacceptably complicated. Our test harness code is now significantly larger than the actual tests themselves, and the amount of boilerplate is frustrating. Verbose test suites are especially bad, since forcing programmers to jump through hoops just to implement a single test reduces the likelihood that people will actually write good tests, if they write tests at all. In contrast, if writing tests is easy, then people will naturally write more of them.</p><p>The above strategy to writing tests is not good enough, but it does reveal a particular problem: in Haskell, typeclass instances are not first-class values that can be manipulated and abstracted over, they are static constructs that can only be managed by the compiler, and users do not have a direct way to modify them. With some cleverness, however, we can actually create an approximation of first-class typeclass dictionaries, which will allow us to dramatically simplify the above testing mechanism.</p><h2><a name="creating-first-class-typeclass-instances"></a>Creating first-class typeclass instances</h2><p>In order to provide an easy way to construct instances, we need a way to represent instances as ordinary Haskell values. This is not terribly difficult, given that instances are conceptually just records containing a collection of functions. For example, we could create a datatype that represents an instance of the <code>MonadFS</code> typeclass:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>To avoid namespace clashes with the actual method identifiers, the record fields are prefixed with an underscore, but otherwise, the translation is remarkably straightforward. Using this record type, we can easily create values that represent the two instances we defined above:</p><pre><code class="pygments"><span class="nf">contentInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> + +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>These two values represent two different implementations of <code>MonadFS</code>, but since they’re ordinary Haskell values, they can be manipulated and even <em>extended</em> like any other records. This can be extremely useful, since it makes it possible to create a sort of “base” instance, then have individual test cases override individual pieces of functionality piecemeal.</p><p>Of course, although we’ve written these two instances, we have no way to actually use them. After all, Haskell does not provide a way to explicitly provide typeclass dictionaries. Fortunately, we can create a sort of “proxy” type that will use a reader to thread the dictionary around explicitly, and the instance can defer to the dictionary’s implementation.</p><h3><a name="creating-an-instance-proxy"></a>Creating an instance proxy</h3><p>To represent our proxy type, we’ll use a combination of a <code>Writer</code> and a <code>ReaderT</code>; the former to implement the logging used by instances, and the latter to actually thread around the dictionary. Our type will look like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">log</span> +<span class="w"> </span><span class="p">)</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="n">inst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">inst</span><span class="p">)</span></code></pre><p>This might look rather complicated, and it is, but let’s break down exactly what it’s doing.</p><ol><li><p>The <code>TestM</code> type includes two type parameters. The first is the type of value that will be logged (hence the name <code>log</code>), which corresponds to the argument to <code>Writer</code> from previous incarnations of <code>TestM</code>. Unlike those types, though, we want this version to work with any <code>Monoid</code>, so we’ll make it a type parameter. The second parameter is simply the type of the current monadic value, as before.</p></li><li><p>The type itself is defined as a wrapper around a small monad transformer stack, the first of which is <code>ReaderT</code>. The state threaded around by the reader is, in this case, the instance dictionary, which is <code>MonadFSInst</code>.</p><p>However, recall that <code>MonadFSInst</code> accepts a type variable—the type of a monad itself—so we must provide <code>TestM log</code> as an argument to <code>MonadFSInst</code>. This slight bit of indirection allows us to tie the knot between the mutually dependent instances and proxy type.</p></li><li><p>The base monad in the transformer stack is <code>Writer</code>, which is used to actually implement the logging functionality, just like in prior cases. The only difference now is that the <code>log</code> type parameter now determines what the writer actually produces.</p></li><li><p>Finally, as before, we use <code>GeneralizedNewtypeDeriving</code> to derive all the relevant <code>mtl</code> classes, adding the somewhat wordy <code>MonadReader</code> constraint to the list.</p></li></ol><p>Using this single type, we can now implement a <code>MonadFS</code> instance that defers to the dictionary carried around within <code>TestM</code>’s reader state:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_readFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_writeFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This may seem somewhat boilerplate-y, and it is to some extent, but the important consideration is that this boilerplate only needs to be written <em>once</em>. With this in place, it’s now possible to write an arbitrary number of first-class instances that use the above mechanism without extending the mechanism at all.</p><p>To see what actually using this code would look like, let’s update the <code>reverseFile</code> tests to use the new <code>TestM</code> implementation, as well as the <code>contentInst</code> and <code>pathInst</code> dictionaries from earlier:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>We can do a little bit better, though. Really, the definitions of <code>contentInst</code> and <code>pathInst</code> are specific to each test case. With ordinary typeclass instances, we cannot scope them to any particular block, but since <code>MonadFSInst</code> is just an ordinary Haskell datatype, we can manipulate them just like any other Haskell values. Therefore, we can just inline those instances’ definitions into the test cases themselves to keep them closer to the actual tests.</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This is pretty good. We’re now able to create inline instances of our <code>MonadFS</code> typeclass, which allows us to write extremely concise unit tests using ordinary Haskell typeclasses as system seams. We’ve managed to cut down on the boilerplate considerably, though we still have a couple problems. For one, this example only uses a single typeclass containing only two methods. A real <code>MonadFS</code> typeclass would likely have at least a dozen methods for performing various filesystem operations, and writing out the instance dictionaries for every single method, even the ones that aren’t used within the code under test, would be pretty frustratingly verbose.</p><p>This problem is solvable, though. Since instances are just ordinary Haskell records, we can create a “base” instance that just throws an exception whenever the method is called:</p><pre><code class="pygments"><span class="nf">baseInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">baseInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_readFile’"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_writeFile’"</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Then code that only uses <code>readFile</code> could only override that particular method, for example:</p><pre><code class="pygments"><span class="kr">let</span><span class="w"> </span><span class="n">myInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">baseInst</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span></code></pre><p>Normally, of course, this would be a terrible idea. However, since this is all just test code, it can be extremely useful in quickly figuring out what methods need to be stubbed out for a particular test case. Since all the code actually gets run at test time, attempts to use unimplemented instance methods will immediately raise an error, informing the programmer which methods need to be implemented to make the test pass. This can also help to significantly cut down on the amount of effort it takes to implement each test.</p><p>Another problem is that our approach is specialized exclusively to <code>MonadFS</code>. What about functions that use both <code>MonadFS</code> <em>and</em> <code>MonadDB</code>, for example? Fortunately, that is not hard to solve, either. We can adapt the <code>MonadFSInst</code> type to include fields for all of the typeclasses relevant to our system, turning it into a generic test fixture of sorts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FixtureInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FixtureInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">-- MonadFS</span> +<span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="c1">-- MonadDB</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Updating <code>TestM</code> to use <code>FixtureInst</code> instead of <code>MonadFSInst</code> is trivial, and all the rest of the infrastructure still works. However, this means that every time a new typeclass is added, three things need to be updated:</p><ol><li><p>Its methods need to be added to the <code>FixtureInst</code> record.</p></li><li><p>Those methods need to be given error-raising defaults in the <code>baseInst</code> value.</p></li><li><p>An actual instance of the typeclass needs to be written for <code>TestM</code> that defers to the <code>FixtureInst</code> value.</p></li></ol><p>Furthermore, most of this manual manipulation of methods is required every time a particular typeclass changes, whether that means adding a method, removing a method, renaming a method, or changing a method’s type. This is especially frustrating given that all this code is really just mechanical boilerplate that could all be derived by the set of typeclasses being tested.</p><p>That last point is especially important: aside from the instances themselves, every piece of boilerplate above is obviously possible to generate from existing types alone. With that piece of information in mind, we can do even better: we can use Template Haskell.</p><h2><a name="removing-the-boilerplate-using-test-fixture"></a>Removing the boilerplate using <code>test-fixture</code></h2><p>The above code was not only rather boilerplate-heavy, it was pretty complicated. Fortunately, you don’t actually have to write it. Enter the library <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code></a>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture.TH</span> + +<span class="nf">mkFixture</span><span class="w"> </span><span class="s">"FixtureInst"</span><span class="w"> </span><span class="p">[</span><span class="kt">&#39;&#39;MonadFS</span><span class="p">,</span><span class="w"> </span><span class="kt">&#39;&#39;MonadDB</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">contentInst</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">pathInst</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p><strong>That’s it.</strong> The above code automatically generates everything you need to write fast, simple, deterministic unit tests in Haskell. The <code>mkFixture</code> function is a Template Haskell macro that expands into a definition quite similar to the <code>FixtureInst</code> type we wrote by hand, but since it’s automatically generated from the typeclass definitions, it never needs to be updated.</p><p>The <code>logTestFixture</code> function replaces the <code>logTestM</code> function we wrote by hand, but it works exactly the same. The <code>Control.Monad.TestFixture</code> library also exports a <code>log</code> function that is a synonym for <code>tell . singleton</code>, but using <code>tell</code> directly still works if you prefer.</p><p>The <code>mkFixture</code> function also generates a <code>Default</code> instance, which replaces the <code>baseInst</code> value defined earlier. It functions the same way, though, producing useful error messages that refer to the names of unimplemented typeclass methods that have not been stubbed out.</p><p>This blog post is not a <code>test-fixture</code> tutorial—indeed, it is much more complicated than a <code>test-fixture</code> tutorial would be, since it covers what the library is really doing under the hood—but if you’re interested, I would highly recommend you take a look at the <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code> documentation on Hackage</a>.</p><h2><a name="conclusion-credits-and-similar-techniques"></a>Conclusion, credits, and similar techniques</h2><p>This blog post came about as the result of a need my coworkers and I found when writing Haskell code; we wanted a way to write unit tests quickly and easily, but we didn’t find much advice from the rest of the Haskell ecosystem. The <code>test-fixture</code> library is the result of that exploratory work, and we currently use it to test a significant portion of our Haskell code.</p><p>It would be extremely unfair to suggest that I was the inventor of this technique or the inventor of the library. Two of my coworkers, <a href="https://github.com/jxv">Joe Vargas</a> and <a href="https://github.com/aztecrex">Greg Wiley</a>, came up with the general approach and wrote <code>Control.Monad.TestFixture</code>, and I simply wrote the Template Haskell macro to eliminate the boilerplate. With that in mind, I think I can say with some fairness that I think this technique is a joy to use when unit testing is a desirable goal, and I would definitely recommend it if you are interested in doing isolated testing in Haskell.</p><p>The general technique of using typeclasses to emulate effects was in part inspired by the well-known <code>mtl</code> library. An alternate approach to writing unit-testable Haskell code is using free monads, but overall, I prefer this approach over free monads because the typeclass constraints add type safety in ways that free monads do not (at least not without additional boilerplate), and this approach also lends itself well to static analysis-based boilerplate reduction techniques. It has its own tradeoffs, though, so if you’ve had success with free monads, then I certainly make no claim this is a superior approach, just one that I’ve personally found pleasant.</p><p>As a final note, if you <em>do</em> check out <code>test-fixture</code>, feel free to leave feedback by opening issues on <a href="https://github.com/cjdev/test-fixture/issues">the GitHub issue tracker</a>—even things like confusing documentation are worth a bug report.</p><ol class="footnotes"></ol></article>Understanding the npm dependency model2016-08-24T00:00:00Z2016-08-24T00:00:00ZAlexis King<article><p>Currently, <a href="https://www.npmjs.com">npm</a> is <em>the</em> package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like <a href="https://bower.io">Bower</a> are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects <strong>you</strong> as a user or package developer.</p><h2><a name="first-the-basics"></a>First, the basics</h2><p>At a high level, npm is not too dissimilar from other package managers for programming languages: packages depend on other packages, and they express those dependencies with <em>version ranges</em>. npm happens to use the <a href="http://semver.org">semver</a> versioning scheme to express those ranges, but the way it performs version resolution is mostly immaterial; what matters is that packages can depend on ranges rather than specific versions of packages.</p><p>This is rather important in any ecosystem, since locking a library to a specific set of dependencies could cause significant problems, but it’s actually much less of a problem in npm’s case compared to other, similar package systems. Indeed, it is often safe for a library author to pin a dependency to a specific version without affecting dependent packages or applications. The tricky bit is determining <em>when</em> this is safe and when it’s not, and this is what I so frequently find that people get wrong.</p><h2><a name="dependency-duplication-and-the-dependency-tree"></a>Dependency duplication and the dependency tree</h2><p>Most users of npm (or at least most package authors) eventually learn that, unlike other package managers, npm installs a <em>tree</em> of dependencies. That is, every package installed gets its own set of dependencies rather than forcing every package to share the same canonical set of packages. Obviously, virtually every single package manager in existence has to model a dependency tree at some point, since that’s how dependencies are expressed by programmers.</p><p>For example, consider two packages, <code>foo</code> and <code>bar</code>. Each of them have their own set of dependencies, which can be represented as a tree:</p><pre><code>foo +├── hello ^0.1.2 +└── world ^1.0.7 + +bar +├── hello ^0.2.8 +└── goodbye ^3.4.0 +</code></pre><p>Imagine an application that depends on <em>both</em> <code>foo</code> and <code>bar</code>. Obviously, the <code>world</code> and <code>goodbye</code> dependencies are totally unrelated, so how npm handles them is relatively uninteresting. However, consider the case of <code>hello</code>: both packages require conflicting versions.</p><p>Most package managers (including RubyGems/Bundler, pip, and Cabal) would simply barf here, reporting a version conflict. This is because, in most package management models, <strong>only one version of any particular package can be installed at a time</strong>. In that sense, one of the package manager’s primary responsibilities is to figure out a set of package versions that will satisfy every version constraint simultaneously.</p><p>In contrast, npm has a somewhat easier job: it’s totally okay with installing different versions of the same package because each package gets its own set of dependencies. In the aforementioned example, the resulting directory structure would look something like this:</p><pre><code>node_modules/ +├── foo/ +│ └── node_modules/ +│ ├── hello/ +│ └── world/ +└── bar/ + └── node_modules/ + ├── hello/ + └── goodbye/ +</code></pre><p>Notably, the directory structure very closely mirrors the actual dependency tree. The above diagram is something of a simplification: in practice, each transitive dependency would have its own <code>node_modules</code> directory and so on, but the directory structure can get pretty messy pretty quickly. (Furthermore, npm 3 performs some optimizations to attempt to share dependencies when it can, but those are ultimately unnecessary to actually understanding the model.)</p><p>This model is, of course, extremely simple. The obvious effect is that every package gets its own little sandbox, which works absolutely marvelously for utility libraries like <code>ramda</code>, <code>lodash</code>, or <code>underscore</code>. If <code>foo</code> depends on <code>ramda@^0.19.0</code> but <code>bar</code> depends on <code>ramda@^0.22.0</code>, they can both coexist completely peacefully without any problems.</p><p>At first blush, this system is <em>obviously</em> better than the alternative, flat model, so long as the underlying runtime supports the required module loading scheme. However, it is not without drawbacks.</p><p>The most apparent downside is a significant increase in code size, given the potential for many, many copies of the same package, all with different versions. An increase in code size can often mean more than just a larger program—it can have a significant impact on performance. Larger programs just don’t fit into CPU caches as easily, and merely having to page a program in and out can significantly slow things down. That’s mostly just a tradeoff, though, since you’re sacrificing performance, not program correctness.</p><p>The more insidious problem (and the one that I see crop up quite a lot in the npm ecosystem without much thought) is how dependency isolation can affect cross-package communication.</p><h2><a name="dependency-isolation-and-values-that-pass-package-boundaries"></a>Dependency isolation and values that pass package boundaries</h2><p>The earlier example of using <code>ramda</code> is a place where npm’s default dependency management scheme really shines, given that Ramda just provides a bunch of plain ol’ functions. Passing these around is totally harmless. In fact, mixing functions from two different versions of Ramda would be totally okay! Unfortunately, not all cases are nearly that simple.</p><p>Consider, for a moment, <code>react</code>. React components are very much <em>not</em> plain old data; they are complex values that can be extended, instantiated, and rendered in a variety of ways. React represents component structure and state using an internal, private format, using a mixture of carefully arranged keys and values and some of the more powerful features of JavaScript’s object system. This internal structure might very well change between React versions, so a React component defined with <code>react@0.3.0</code> likely won’t work quite right with <code>react@15.3.1</code>.</p><p>With that in mind, consider two packages that define their own React components and export them for consumers to use. Looking at their dependency tree, we might see something like this:</p><pre><code>awesome-button +└── react ^0.3.0 + +amazing-modal +└── react ^15.3.1 +</code></pre><p>Given that these two packages use wildly different versions of React, npm would give each of them their own copy of React, as requested, and packages would happily install. However, if you tried to use these components together, they wouldn’t work at all! A newer version of React simply cannot understand an old version’s component, so you would get a (likely confusing) runtime error.</p><p>What went wrong? Well, dependency isolation works great when a package’s dependencies are purely implementation details, never observable from outside of a package. However, as soon as a package’s dependency becomes exposed as part of its <em>interface</em>, dependency isolation is not only subtly wrong, it can cause complete failure at runtime. These are cases when traditional dependency management are much better—they will tell you as soon as you attempt to install two packages that they just don’t work together, rather than waiting for you to figure that out for yourself.</p><p>This might not sound <em>too</em> bad—after all, JavaScript is a very dynamic language, so static guarantees are mostly few and far between, and your tests should catch these problems should they arise—but it can cause unnecessary issues when two packages <em>can</em> theoretically work together fine, but because npm assigned each one its own copy of a particular package (that is, it wasn’t quite smart enough to figure out it could give them both the same copy), things break down.</p><p>Looking outside of npm specifically and considering this model when applied to other languages, it becomes increasingly clear that this won’t do. This blog post was inspired by <a href="https://www.reddit.com/r/haskell/comments/4zc6y3/why_doesnt_cabal_use_a_model_like_that_of_npm/?ref=share&amp;ref_source=link">a Reddit thread discussing the npm model applied to Haskell</a>, and this flaw was touted as a reason why it couldn’t possibly work for such a static language.</p><p>Due to the way the JavaScript ecosystem has evolved, it’s true that most people can often get away with this subtle potential for incorrect behavior without any problems. Specifically, JavaScript tends to rely on duck typing rather than more restrictive checks like <code>instanceof</code>, so objects that satisfy the same protocol will still be compatible, even if their implementations aren’t <em>quite</em> the same. However, npm actually provides a robust solution to this problem that allows package authors to explicitly express these “cross-interface” dependencies.</p><h3><a name="peer-dependencies"></a>Peer dependencies</h3><p>Normally, npm package dependencies are listed under a <code>"dependencies"</code> key in the package’s <code>package.json</code> file. There is, however, another, less-used key called <code>"peerDependencies"</code>, which has the same format as the ordinary dependencies list. The difference shows up in how npm performs dependency resolution: rather than getting its own copy of a peer dependency, a package expects that dependency to be provided by its dependent.</p><p>This effectively means that peer dependencies are effectively resolved using the “traditional” dependency resolution mechanism that tools like Bundler and Cabal use: there must be one canonical version that satisfies everyone’s constraint. Since npm 3, things are a little bit less straightforward (specifically, peer dependencies are not automatically installed unless a dependent package explicitly depends on the peer package itself), but the basic idea is the same. This means that package authors must make a choice for each dependency they install: should it be a normal dependency or a peer dependency?</p><p>This is where I think people tend to get a little lost, even those familiar with the peer dependency mechanism. Fortunately, the answer is relatively simple: is the dependency in question visible in <em>any place</em> in the package’s interface?</p><p>This is sometimes hard to see in JavaScript because the “types” are invisible; that is, they are dynamic and rarely explicitly written out. However, just because the types are dynamic does not mean they are not there at runtime (and in the heads of various programmers), so the rule still holds: if the type of a function in a package’s public interface somehow depends on a dependency, it should be a peer dependency.</p><p>To make this a little more concrete, let’s look at a couple of examples. First off, let’s take a look at some simple cases, starting with some uses of <code>ramda</code>:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">merge</span><span class="p">,</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;ramda&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">withDefaultConfig</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">config</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">merge</span><span class="p">({</span><span class="w"> </span><span class="nx">path</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;.&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">config</span><span class="p">)</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">add5</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">add</span><span class="p">(</span><span class="mf">5</span><span class="p">)</span></code></pre><p>The first example here is pretty obvious: in <code>withDefaultConfig</code>, <code>merge</code> is used purely as an implementation detail, so it’s safe, and it’s not part of the module’s interface. In <code>add5</code>, the example is a little trickier: the result of <code>add(5)</code> is a partially-applied function created by Ramda, so technically, a Ramda-created value is a part of this module’s interface. However, the contract <code>add5</code> has with the outside world is simply that it is a JavaScript function that adds five to its argument, and it doesn’t depend on any Ramda-specific functionality, so <code>ramda</code> can safely be a non-peer dependency.</p><p>Now let’s look at another example using the <code>jpeg</code> image library:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">Jpeg</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;jpeg&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createSquareBuffer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createSquareJpeg</span><span class="p">(</span><span class="nx">size</span><span class="p">).</span><span class="nx">encode</span><span class="p">(</span><span class="nx">cb</span><span class="p">)</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createSquareJpeg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">size</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Jpeg</span><span class="p">(</span><span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="nx">size</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="mf">0</span><span class="p">),</span><span class="w"> </span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="nx">size</span><span class="p">)</span></code></pre><p>In this case, the <code>createSquareBuffer</code> function invokes a callback with an ordinary Node.js <code>Buffer</code> object, so the <code>jpeg</code> library is an implementation detail. If that were the only function exposed by this module, <code>jpeg</code> could safely be a non-peer dependency. However, the <code>createSquareJpeg</code> function violates that rule: it returns a <code>Jpeg</code> object, which is an opaque value with a structure defined exclusively by the <code>jpeg</code> library. Therefore, a package with the above module <em>must</em> list <code>jpeg</code> as a peer dependency.</p><p>This sort of restriction works in reverse, too. For example, consider the following module:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">writeFile</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;fs&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">writeJpeg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span><span class="w"> </span><span class="nx">jpeg</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">jpeg</span><span class="p">.</span><span class="nx">encode</span><span class="p">((</span><span class="nx">image</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span><span class="w"> </span><span class="nx">image</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">))</span></code></pre><p>The above module does not even <em>import</em> the <code>jpeg</code> package, yet it implicitly depends on the <code>encode</code> method of the <code>Jpeg</code> interface. Therefore, despite not even explicitly using it anywhere in the code, a package containing the above module should include <code>jpeg</code> as a peer dependency.</p><p>They key is to carefully consider what contract your modules have with their dependents. If those contracts involve other packages in any way, they should be peer dependencies. If they don’t, they should be ordinary dependencies.</p><h2><a name="applying-the-npm-model-to-other-programming-languages"></a>Applying the npm model to other programming languages</h2><p>The npm model of package management is more complicated than that of other languages, but it provides a real advantage: implementation details are kept as implementation details. In other systems, it’s quite possible to find yourself in “dependency hell”, when you personally know that the version conflict reported by your package manager is not a real problem, but because the package system must pick a single canonical version, there’s no way to make progress without adjusting code in your dependencies. This is extremely frustrating.</p><p>This sort of dependency isolation is not the most advanced form of package management in existence—indeed, far from it—but it’s definitely more powerful than most other mainstream systems out there. Of course, most other languages could not adopt the npm model simply by changing the package manager: having a global package namespace can prevent multiple versions of the same package being installed at a <em>runtime</em> level. The reason npm is able to do what it does is because Node itself supports it.</p><p>That said, the dichotomy between peer and non-peer dependencies is a little confusing, especially to people who aren’t package authors. Figuring out which packages need to go in which group is not always obvious or trivial. Fortunately, other languages might be able to help.</p><p>Returning to Haskell, its strong static type system would potentially allow this distinction to be detected entirely automatically, and Cabal could actually report an error when a package used in an exposed interface was not listed as a peer dependency (much like how it currently prevents importing a transitive dependency without explicitly depending on it). This would allow helper function packages to keep on being implementation details while still maintaining strong interface safety. This would likely take a lot of work to get just right—managing the global nature of typeclass instances would likely make this much more complicated than a naïve approach would accommodate—but it would add a nice layer of flexibility that does not currently exist.</p><p>From the perspective of JavaScript, npm has demonstrated that it can be a capable package manager, despite the monumental burden placed upon it by the ever-growing, ever-changing JS ecosystem. As a package author myself, I would implore other users to carefully consider the peer dependencies feature and work hard to encode their interfaces’ contracts using it—it’s a commonly misunderstood gem of the npm model, and I hope this blog post helped to shed at least a little more light upon it.</p><ol class="footnotes"></ol></article>Climbing the infinite ladder of abstraction2016-08-11T00:00:00Z2016-08-11T00:00:00ZAlexis King<article><p>I started programming in elementary school.</p><p>When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to <a href="https://xkcd.com/974/">solve the general problem</a>. When I learned about programming, I was immediately hooked: it was <em>so easy</em> to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.</p><p>Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of <strong>abstraction</strong>.</p><h2><a name="the-brick-wall-of-inexpressiveness"></a>The brick wall of inexpressiveness</h2><p>When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:</p><pre><code class="pygments"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="p">;</span> +<span class="p">}</span> + +<span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span> +<span class="p">}</span></code></pre><p>This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.</p><p>It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The <em>whole reason</em> I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.</p><p>When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.</p><p>Both of these languages were flawed, as all languages are, but they opened my mind to the idea that <em>programming languages</em> could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.</p><h2><a name="discovering-lisp"></a>Discovering Lisp</h2><p>Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.</p><p>This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?</p><p>The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a <a href="https://github.com/lexi-lambda/libsol">highly dynamic Lisp with a prototypal object system called Sol</a>. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.</p><p>Unfortunately, it was also abysmally slow.</p><p>I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called <a href="http://racket-lang.org">Racket</a>. At about the same time, someone pointed me to a totally different language called <a href="https://www.haskell.org">Haskell</a>. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.</p><h2><a name="a-journey-into-complexity"></a>A journey into complexity</h2><p>Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned <em>so much more</em> since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.</p><p>I’ve also started realizing something else, though: <strong>the languages I’ve settled into are <em>really complicated</em>.</strong></p><p>When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.</p><p>Why?</p><p>Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”</p><p>Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?</p><p>I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.</p><p>Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I <em>still</em> have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?</p><h3><a name="improvement-but-never-mastery"></a>Improvement, but never mastery</h3><p>Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.</p><p>For example I am pretty confident that I <em>get</em> JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques <em>within</em> the language, not entirely new linguistic constructs.</p><p>On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about <code>TypeInType</code>? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do <em>not</em> have a good grasp on all the intricacies of Racket’s macro system.</p><p>This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out <code>DataKinds</code>, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.</p><p>Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is <em>terrible</em>, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.</p><p>Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even <em>more</em> advanced things, and still, none of them truly solve my problems.</p><h2><a name="ultimately-it-all-has-at-least-a-little-value"></a>Ultimately, it all has (at least a little) value</h2><p>It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.</p><p>The scariest part of all is that I think it’s completely worthwhile.</p><p>So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs <em>seem</em> unnecessarily complicated, they <em>seem</em> increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.</p><p>The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.</p><p>Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:</p><pre><code class="pygments"><span class="nl">10</span><span class="w"> </span><span class="kr">INPUT</span><span class="w"> </span><span class="s2">"What is your name: "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span> +<span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">"Hello "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span></code></pre><p>On the other hand, very few would probably understand this one:</p><pre><code class="pygments"><span class="c1">-- | A class for categories.</span> +<span class="c1">-- id and (.) must form a monoid.</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">Category</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="c1">-- | the identity morphism</span> +<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="c1">-- | morphism composition</span> +<span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">c</span></code></pre><p>Yet very few new programs are being written in BASIC, and lots are being written in Haskell.</p><p>Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:</p><pre><code class="pygments"><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composeWithProps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">curry</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">childProps</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">omit</span><span class="p">([</span><span class="s1">&#39;children&#39;</span><span class="p">],</span><span class="w"> </span><span class="nx">childProps</span><span class="p">),</span><span class="w"> </span><span class="nx">childProps</span><span class="p">.</span><span class="nx">children</span><span class="p">));</span> +<span class="w"> </span><span class="c1">// give the composed component a pretty display name for debugging</span> +<span class="w"> </span><span class="nx">composed</span><span class="p">.</span><span class="nx">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`Composed(</span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="si">}</span><span class="sb">, </span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">composed</span><span class="p">;</span> +<span class="p">});</span></code></pre><p>Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.</p><p>That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an <em>extremely</em> productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.</p><h3><a name="reactionary-anti-intellectualism-and-the-search-for-moderation"></a>Reactionary anti-intellectualism and the search for moderation</h3><p>I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. <a href="https://twitter.com/lexi_lambda/status/763111451691134976">I recently tweeted a picture of some rather dense mathematics from a paper I’d read</a>, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.</p><p>Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like <code>map</code> should be banned because they are too confusing and not immediately self-explanatory.</p><p>I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.</p><p>Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern <em>technology</em> possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.</p><p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.</p><p>I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.</p><p>Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.</p><ol class="footnotes"></ol></article>Four months with Haskell2016-06-12T00:00:00Z2016-06-12T00:00:00ZAlexis King<article><p>At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.</p><p>Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an <em>incredible</em> programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.</p><h2><a name="dispelling-some-myths"></a>Dispelling some myths</h2><p>Before moving on and discussing my experiences in depth, I want to take a quick detour to dispel some frequent rumors I hear about why Haskell is at least potentially problematic. These are things I hear a <em>lot</em>, and nothing in my experience so far would lead me to believe these are actually true. Ultimately, I don’t want to spend too much time on these—I think that, for the most part, they are nitpicks that people complain about to avoid understanding the deeper and more insidious problems with the language—but I think it’s important to at least mention them.</p><h3><a name="hiring-haskell-developers-is-not-hard"></a>Hiring Haskell developers is not hard</h3><p>I am on the first Haskell team in my company, and I am among the first Haskell developers we ever hired. Not only were we hiring without much experience with Haskell at all, we explicitly <em>did not</em> want to hire remote. Debate all you like about whether or not permitting remote work is a good idea, but I don’t think anyone would dispute that this constraint makes hiring much harder. We didn’t have any trouble finding a very large stream of qualified applicants, and it definitely seems to have dispelled any fears that we would have trouble finding new candidates in the future.</p><h3><a name="performing-i-o-in-haskell-is-easy"></a>Performing I/O in Haskell is easy</h3><p>Haskell’s purity is a point of real contention, and it’s one of the most frustrating complaints I often hear about Haskell. It is surprisingly common to hear concerns along the lines of “I don’t want to use Haskell because its academic devotion to purity sounds like it would make it very hard to get anything done”. There are very valid reasons to avoid Haskell, but in practice, I/O is not one of them. In fact, I found that isolating I/O in Haskell was much the same as isolating I/O in every other language, which I need to do anyway to permit unit testing.</p><p>...you <em>do</em> write deterministic unit tests for your impure logic, right?</p><h3><a name="working-with-lots-of-monads-is-not-very-difficult"></a>Working with lots of monads is not very difficult</h3><p>The “M word” has ended up being a running joke <em>about</em> Haskell that actually ends up coming up fairly rarely <em>within</em> the Haskell community. To be clear, there is <em>no doubt</em> in my mind that monads make Haskell intimidating and provide a steep learning curve for new users. The proliferation of the joke that monads are impossible to explain, to the point of becoming mythologized, is absolutely indicative of a deeper problem about Haskell’s accessibility. However, once people learn the basics about monads, I’ve found that applying them is just as natural as applying any other programming pattern.</p><p>Monads are used to assist the programmer, not impede them, and they really do pay off in practice. When something has a monadic interface, there’s a decent chance I already know what that interface is going to do, and that makes working with lots of different monads surprisingly easy. Admittedly, I do rely very, very heavily on tooling to help me out here, but with things like mouseover type tooltips, I’ve actually found that working with a variety of different monads and monad transformers is actually quite pleasant, and it makes things very readable!</p><h2><a name="haskell-the-good-parts"></a>Haskell: the good parts</h2><p>With the disclaimers out of the way, I really just want to gush for a little bit. This is not going to be an objective, reasoned survey of why Haskell is good. I am not even really going to touch upon why types are so great and why purity is so wonderful—I’d love to discuss those in depth, but that’s for a different blog post. For now, I just want to touch upon the real surprises, the real things that made me <em>excited</em> about Haskell in ways I didn’t expect. These are the things that my subjective little experience has found fun.</p><h3><a name="language-extensions-are-haskell"></a>Language extensions <em>are</em> Haskell</h3><p>There was a time in my life when I spent a lot of time writing C. There are a lot of compilers for C, and they all implement the language in subtly different but often incompatible ways, especially on different platforms. The only way to maintain a modicum of predictability was to adhere to the standards <em>religiously</em>, even when certain GCC or MSVC extensions seem tantalizingly useful. I was actually bitten a few times by real instances where I figured I’d just use a harmless extension that was implemented everywhere, then found out it worked slightly differently across different compilers in a particular edge case. It was a learning experience.</p><p>It seems that this fear provides a very real distrust for using GHC’s numerous <em>language extensions</em>, and indeed, for a long time, I felt that it was probably an admirable goal to stick to Haskell 98 or Haskell 2010 as closely as possible. Sometimes I chose a slightly more verbose solution that was standard Haskell to avoid turning on a trivial extension that would make the code look a little bit cleaner.</p><p>About a year later, I’m finding that attitude was not only a mistake, but it forced me to often completely miss out on a lot of Haskell’s core value. GHC <em>won</em>, and now GHC and Haskell are basically synonymous. With that in mind, the portability concerns of language extensions are a bit of a non-issue, and turning them on is a very good idea! Some extensions are more than a little dangerous, so they cannot all be turned on without thinking, but the question is absolutely not “Is using language extensions a good idea?” and more “Is using <em>this</em> language extension a good idea?”</p><p>This is important, and I bring it up for a reason: so much of the awesomeness of Haskell is locked behind language extensions. Turning a lot of these on is one of the main things that made me really start to see how incredibly powerful Haskell actually is.</p><h3><a name="phantom-types"></a>Phantom types</h3><p>I’m going to start out by talking about <em>phantom types</em>, which are a pretty simple concept but a powerful one, and they serve as the foundation for a lot of other cool type-level tricks that can make Haskell extremely interesting. The basic idea of a phantom type is simple; it’s a type parameter that isn’t actually used to represent any particular runtime value:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type represents an id for some kind of value, but although the kind of value is specified in the type as the <code>a</code> type parameter, it isn’t actually used anywhere on the data definition—no matter what <code>a</code> is, an <code>Id</code> is just a piece of text. This makes it possible to write functions that operate on specific kinds of ids, and those invariants will be statically checked by the compiler, even though the runtime representation is entirely identical:</p><pre><code class="pygments"><span class="nf">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span></code></pre><p>Using <code>FlexibleInstances</code>, it’s also possible to create different instances for different kinds of ids. For example, it would be possible to have different <code>Show</code> instances depending on the type of id in question.</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"user #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"post #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span></code></pre><p>This provides a simple framework for encoding entirely arbitrary information into the type system, then asking the compiler to actually check assertions about that information. This is made even more powerful with some other extensions, which I’ll talk about shortly.</p><h3><a name="letting-the-compiler-write-code"></a>Letting the compiler write code</h3><p>One of the things I really dislike, more than most things, is boilerplate. A little bit of boilerplate is fine—even necessary at times—but as soon as I start wondering if a code generator would improve things, I think the programming language has pretty much failed me.</p><p>I write a lot of Racket because, in a sense, Racket is the ultimate boilerplate killer: the macro system is a first-class code generator integrated with the rest of the language, and it means that boilerplate is almost never an issue. Of course, that’s not always true: sometimes a bit of boilerplate <em>is</em> still necessary because macros cannot deduce enough information about the program to generate the code entirely on their own, and in Haskell, some of that information is actually present in the type system.</p><p>This leads to two absolutely incredible extensions, both of which are simple and related, but which actually <em>completely change</em> how I approach problems when programming. These two extensions are <code>GeneralizedNewtypeDeriving</code> and <code>StandaloneDeriving</code>.</p><h4><a name="newtypes-and-type-safety"></a>Newtypes and type safety</h4><p>The basic idea is that “newtypes” are just simple wrapper types in Haskell. This turns out to be extremely important when trying to find the value of Haskell because they allow you to harden type safety by specializing types to <em>your</em> domain. For example, consider a type representing a user’s name:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type is extremely simple, and in fact isn’t even at all different from a simple <code>Text</code> value with respect to its representation, since all combinations of unicode characters are allowed in a name. Therefore, what’s the point of a separate type? Well, this allows Haskell to introduce actual compilation failures when two different kinds of textual data are mixed. This is not a new idea, and even in languages that don’t support this sort of thing, Joel Spolsky’s old blog post <a href="http://www.joelonsoftware.com/articles/Wrong.html">Making Wrong Code Look Wrong</a> describes how it can be done by convention. Still, almost every modern language makes this possible: in C, it would be a single-member <code>struct</code>, in class-based OO languages, it would be a single-member class... this is not a complicated idea.</p><p>The difference lies in its usage. In other languages, this strategy is actually not very frequently employed for the simple reason that it is almost always extremely annoying. You are forced to do tons of wrapping/unwrapping, and at that point it isn’t really clear if you’re even getting all that much value out of the distinction when your first solution to a type mismatch is wrapping or unwrapping the value without a second thought. In Haskell, however, this can be heavily mitigated by asking the compiler to <em>automatically derive typeclass implementations</em>, which allow the unwrapping/wrapping to effectively happen implicitly for a constrained set of operations.</p><h4><a name="using-generalizednewtypederiving"></a>Using <code>GeneralizedNewtypeDeriving</code></h4><p>Consider the <code>Name</code> type once again, but this time, let’s derive a class:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">IsString</span><span class="p">)</span></code></pre><p>The <code>IsString</code> typeclass in Haskell allows custom types to automatically be created from string literals. It is <em>not</em> handled specially by Haskell’s <code>deriving</code> mechanism. Since <code>Text</code> implements <code>IsString</code>, an instance will be generated that simply defers to the underlying type, automatically generating the code to wrap the result up in a <code>Name</code> box at the end. This means that code like this will now just magically work:</p><pre><code class="pygments"><span class="nf">name</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Name</span> +<span class="nf">name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa P. Hacker"</span></code></pre><p>No boilerplate needs to be written! This is a neat trick, but it actually turns out to be far more useful than that simple example in practice. What really makes this functionality shine is when you want to derive <em>some</em> kinds of functionality but disallow some others. For example, using the <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a> package, it’s possible to do something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">ToText</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>This creates an opaque <code>Id</code> type, but it automatically generates conversions <em>to</em> textual formats. However, it does <em>not</em> automatically create <code>FromText</code> or <code>FromJSON</code> instances, which would be dangerous because decoding <code>Id</code>s can potentially fail. It’s then possible to write out those instances manually to preserve a type safety:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">FromText</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fromText</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="n">isValidId</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="kr">then</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">String</span><span class="w"> </span><span class="n">val</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">fromText</span><span class="w"> </span><span class="n">val</span><span class="p">)</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span></code></pre><h4><a name="using-standalonederiving"></a>Using <code>StandaloneDeriving</code></h4><p>The ordinary <code>deriving</code> mechanism is extremely useful, especially when paired with the above, but sometimes it is desirable to have a little bit more flexibility. In these cases, <code>StandaloneDeriving</code> can help.</p><p>Take the <code>Id</code> example again: it has a phantom type, and simply adding something like <code>deriving (ToText)</code> with derive <code>ToText</code> instances for <em>all</em> kinds of ids. It is potentially useful, however, to derive instances for more specific id types. Using standalone <code>deriving</code> constructs permits this sort of flexibility.</p><pre><code class="pygments"><span class="kr">deriving</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toText</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">postIdToText</span></code></pre><p>This is an example where GHC language extensions end up becoming significantly more than the sum of their parts, which seems to be a fairly frequent realization. The <code>StandaloneDeriving</code> mechanism is a little bit useful without <code>GeneralizedNewtypeDeriving</code>, but when combined, they are incredibly powerful tools for getting a very fine-grained kind of type safety <em>without</em> writing any boilerplate.</p><h3><a name="datakinds-are-super-cool-with-caveats"></a>DataKinds are super cool, with caveats</h3><p>Phantom types are quite wonderful, but they can only encode <em>types</em>, not arbitrary data. That’s where <code>DataKinds</code> and <code>KindSignatures</code> come in: they allow lifting arbitrary datatypes to the type level so that things that would normally be purely runtime values can be used at compile-time as well.</p><p>The way this works is pretty simple—when you define a datatype, you also define a “datakind”:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Registered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Anonymous</span></code></pre><p>Normally, the above declaration declares a <em>type</em>, <code>RegistrationStatus</code>, and two <em>data constructors</em>, <code>Registered</code> and <code>Anonymous</code>. With <code>DataKinds</code>, it also defines a <em>kind</em>, <code>RegistrationStatus</code>, and two <em>type constructors</em>, <code>Registered</code> and <code>Anonymous.</code></p><p>If that’s confusing, the way to understand that is to realize there is a sort of natural ordering here: types describe values, and kinds describe types. Therefore, turning on <code>DataKinds</code> “lifts” each definition by a single level, so types become kinds and values become types. This permits using these things at the type level:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>In this example, <code>UserId</code> still has a single phantom type variable, <code>s</code>, but this time it is constrained to the <code>RegistrationStatus</code> kind. Therefore, it can <em>only</em> be <code>Registered</code> or <code>Anonymous</code>. This cooperates well with the aforementioned <code>StandaloneDeriving</code> mechanism, and it mostly provides a convenient way to constrain type variables to custom kinds.</p><p>In general, <code>DataKinds</code> is a much more powerful extension, allowing things like type-level natural numbers or strings, which can be used to perform actual type-level computation (especially in combination with <code>TypeFamilies</code>) or a sort of metaprogramming. In some cases, they can even be used to implement things emulating things you can do with dependent types.</p><p>I think <code>DataKinds</code> are a very cool Haskell extension, but there are a couple caveats. One of the main ones is how new kinds are defined: <code>DataKinds</code> “hijacks” the existing datatype declaration syntax by making every single datatype declaration define a type <em>and</em> a kind. This is a little confusing, and it would be nice if a different syntax was used so that each could be defined independently.</p><p>Similarly, it seems that a lot of work is being done to allow using runtime values at the type level, but I wonder if people will ever need to use, say, runtime values at the <em>kind</em> level. This immediately evokes thoughts of Racket’s phase-based macro system, and I wonder if some of this duplication would be unnecessary with something similar.</p><p>Food for thought, but overall, <code>DataKinds</code> are a very nice addition to help with precisely and specifically typing particular problems.</p><h3><a name="typeclasses-can-emulate-effects"></a>Typeclasses can emulate effects</h3><p>This is something that I’ve found interesting in my time writing Haskell because I have <em>no idea</em> if it’s idiomatic or not, but it seems pretty powerful. The initial motivator for this idea was figuring out how to test our code without constantly dropping into <code>IO</code>.</p><p>More generally, we wanted to be able to unit test by “mocking” out collaborators, as it would be described in object oriented programming. I was always semi-distrustful of mocking, and indeed, it seems likely that it is heavily abused in certain circles, but I’ve come to appreciate the need that sometimes it is important to stub things out, <em>even in pure code</em>.</p><p>As an example, consider some code that needs access to the current time. This is something that would normally require <code>IO</code>, but we likely want to be able to use the value in a pure context without “infecting” the entire program with <code>IO</code> types. In Haskell, I have generally seen three ways of handling this sort of thing:</p><ol><li><p>Just inject the required values into the function and produce them “higher up” where I/O is okay. If threading the value around becomes too burdensome, use a Reader monad.</p></li><li><p>Use a free monad or similar to create a pure DSL of sorts, then write interpreters for various implementations, one of which uses <code>IO</code>.</p></li><li><p>Create custom monadic typeclasses that provide interfaces to the functionality you want to perform, then create instances, one of which is an instance over <code>IO</code>.</p></li></ol><p>This last approach seems to be less common in Haskell, but it’s the approach we took, and it seems to work out remarkably well. Returning to the need to get the current time, we could pretty easily write such a typeclass to encode that need:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">UTCTime</span></code></pre><p>Now we can write functions that use the current time:</p><pre><code class="pygments"><span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span><span class="p">)</span></code></pre><p>Now, we can write instances for <code>CurrentTime</code> that will allow us to run the same code in different contexts:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runAppM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">runTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">runTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftIO</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Time</span><span class="o">.</span><span class="kt">Clock</span><span class="o">.</span><span class="n">getCurrentTime</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">posixSecondsToUTCTime</span><span class="w"> </span><span class="mi">0</span></code></pre><p>Where this really starts to shine is when adding additional effects. For example, the above token validation function might also need information about some kind of secret used for signing. Under this model, it’s just another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getTokenSecret</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Secret</span> + +<span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">secret</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getTokenSecret</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span> +<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">verifySignature</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="n">secret</span><span class="p">)</span></code></pre><p>Of course, so far all of these functions have been extremely simple, and we’ve basically been using them as a glorified reader monad. In practice, though, we use this pattern for lots more than just retrieving values. For example, we might have a typeclass for database interactions:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> +<span class="w"> </span><span class="n">insertUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">PersistenceError</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">))</span></code></pre><p>With all of this done, it becomes incredibly easy to see which functions are using which effects:</p><pre><code class="pygments"><span class="nf">postUsers</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">postUsers</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">getHealthcheck</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">getHealthcheck</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>There’s no need to perform any lifting, and this all seems to scale quite nicely. We’ve written some additional utilities to help write tests against functions using these kinds of monadic interfaces, and even though there’s a little bit of annoying boilerplate in a few spots, overall it seems to work quite elegantly.</p><p>I’m not entirely sure how common this is in the Haskell community, but it’s certainly pretty neat how easy it is to get nearly all of the benefits of effect types in other languages simply by composing some of Haskell’s simplest features.</p><h3><a name="atom-s-ide-haskell-tooling-is-invaluable"></a>Atom’s ide-haskell tooling is invaluable</h3><p>Alright, so, confession time: I don’t use Emacs.</p><p>I know, I know, how is that possible? I write Lisp, after all. Well, honestly, I tried picking it up a number of times, but none of those times did I get far enough to ditch my other tools. For Racket work, I use DrRacket, but for almost everything else, I use Atom.</p><p>Atom has a lot of flaws, but it’s also pretty amazing in places, and I absolutely <em>love</em> the Haskell tooling written by the wonderful <a href="https://github.com/atom-haskell">atom-haskell</a> folks. I use it constantly, and even though it doesn’t always work perfectly, it works pretty well. When it has problems, I’ve at least figured out how to get it working correctly.</p><p>This is probably hard to really explain without seeing it for yourself, but I’ve found that I basically <em>depend</em> on this sort of tooling to be fully productive in Haskell, and I have no problem admitting that. The ability to get instant feedback about type errors tied to visual source locations, to be able to directly manipulate the source by selecting expressions and getting type information, and even the option to get inline linter suggestions means I spend a lot less time glancing at the terminal, and even less time in the REPL.</p><p>The tooling is far from perfect, and it leaves a lot to be desired in places (the idea of using that static information for automated, project-wide refactoring <em>a la</em> Java is tantalizing), but most of those things are ideas of what amazing things could be, not broken or missing essentials. I am pretty satisfied with ide-haskell right now, and I can only hope it continues to get better and better.</p><h2><a name="frustrations-drawbacks-and-pain-points"></a>Frustrations, drawbacks, and pain points</h2><p>Haskell is not perfect. In fact, far from it. There is a vast array of little annoyances that I have with the language, as is the case with any language. Still, there are a few overarching problems that I would really like to at least mention. These are the biggest sources of frustration for me so far.</p><h3><a name="purity-failure-and-exception-handling"></a>Purity, failure, and exception-handling</h3><p>One of Haskell’s defining features is its purity—I don’t think many would disagree with that. Some people consider it a drawback, others consider it one of its greatest boons. Personally, I like it a lot, and I think one of the best parts about it is how it requires the programmer to be incredibly deliberate about failure.</p><p>In many languages, when looking up a value from a container where the key doesn’t exist, there are really two ways to go about expressing this failure:</p><ol><li><p>Throw an exception.</p></li><li><p>Return <code>null</code>.</p></li></ol><p>The former is scary because it means <em>any</em> call to any function can make the entire program blow up, and it’s often impossible to know which functions even have the potential to throw. This creates a certain kind of non-local control flow that can sometimes cause a lot of unpredictability. The second option is much the same, especially when any value in a program might be <code>null</code>; it just defers the failure.</p><p>In languages with option types, this is somewhat mitigated. Java now has option types, too, but they are still frequently cumbersome to use because there is nothing like monads to use to simply chain operations together. Haskell, in comparison, has an incredible complement of tools to simply handle errors without a whole lot of burden on the programmer, and I have found that, in practice, this is <em>actually helpful</em> and I really do write better error-handling code.</p><h4><a name="first-the-good-parts"></a>First, the good parts</h4><p>I have seen a comparison drawn between throwing checked exceptions and returning <code>Maybe</code> or <code>Either</code> types, but in practice the difference is massive. Handling checked exceptions is a monotonous chore because they are not first-class values, they are actually entirely separate linguistic constructs. Consider a library that throws a <code>LibraryException</code>, and you want to wrap that library and convert those exceptions to <code>ApplicationException</code>s. Well, have fun writing this code dozens of times:</p><pre><code class="pygments"><span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomething</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span> + +<span class="c1">// ...</span> + +<span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomethingElse</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span></code></pre><p>In Haskell, failure is just represented by first-class values, and it’s totally possible to write helper functions to abstract over that kind of boilerplate:</p><pre><code class="pygments"><span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ApplicationError</span> +<span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">ApplicationError</span><span class="w"> </span><span class="n">a</span> +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapLeft</span><span class="w"> </span><span class="n">libraryToApplication</span></code></pre><p>Now, that same boilerplate-y code becomes nearly invisible:</p><pre><code class="pygments"><span class="nf">x</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomething</span> + +<span class="c1">-- ...</span> + +<span class="nf">y</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomethingElse</span></code></pre><p>This might not <em>seem</em> like much, but it really cuts down on the amount of visual noise, which ends up making all the difference. Boilerplate incurs a cost much bigger than simply taking the time to type it all out (though that’s important, too): the cognitive overhead of parsing which parts of a program are boilerplate has a significant impact on readability.</p><h4><a name="so-what-s-the-problem"></a>So what’s the problem?</h4><p>If error handling is so great in Haskell, then why am I putting it under the complaints section? Well, it turns out that not everyone seems to think it’s as great as I make it out to be because people seem to keep writing Haskell APIs that throw exceptions!</p><p>Despite what some purists would have you believe, Haskell has exceptions, and they are not uncommon. Lots of things can throw exceptions, some of which are probably reasonable. Failing to connect to a database is a pretty catastrophic error, so it seems fair that it would throw. On the other hand, inserting a duplicate record is pretty normal operation, so it seems like that should <em>not</em> throw.</p><p>I mostly treat exceptions in Haskell as unrecoverable catastrophes. If I throw an error in <em>my</em> code, I do not intend to catch it. That means something horrible happened, and I just want that horribleness to show up in a log somewhere so I can fix the problem. If I care about failure, there are better ways to handle that failure gracefully.</p><p>It’s also probably worth noting that exceptions in Haskell can be thrown from anywhere, even pure code, but can only be <em>caught</em> within the <code>IO</code> monad. This is especially scary, but I’ve seen it happen in actual libraries out in the wild, even ones that the entire Haskell ecosystem is built on. One of the crowning examples of this is the <code>text</code> package, which provides a function called <code>decodeUtf8</code> to convert bytestrings into text. Its type is very simple:</p><pre><code class="pygments"><span class="nf">decodeUtf8</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ByteString</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>But wait, what if the bytestring is not actually a valid UTF-8 string?</p><p>Boom. There goes the application.</p><p>Okay, okay, well, at least the <code>text</code> package provides another function, this one called <code>decodeUtf8'</code>, which returns an <code>Either</code>. This is good, and I’ve trained myself to only ever use <code>decodeUtf8'</code>, but it still has some pretty significant problems:</p><ul><li><p>The <em>safe</em> version of this function is the “prime” version, rather than the other way around, which encourages people to use the unsafe one. Ideally, the unsafe one should be explicitly labeled as such... maybe call it <code>unsafeDecodeUtf8</code>?</p></li><li><p>This is not a hypothetical problem. When using a Haskell JWT library, we found a function that converts a string into a JWT. Since not all strings are JWTs, the library intelligently returns a <code>Maybe</code>. Therefore, we figured we were safe.</p><p>A couple weeks later, we found that providing this function with invalid data was returning HTTP 500 errors. Why? Our error handling was meticulous! Well, the answer was a <code>decodeUtf8</code> call, hidden inside of the JWT library. This is especially egregious, given that the API it exposed returned a <code>Maybe</code> anyway! It would have been trivial to use the safe version there, instead, but the poor, misleading name led the library developer to overlook the bug lurking in the otherwise innocuous function.</p><p>Even worse, this function was totally pure, and we used it in pure code, so we could not simply wrap the function and catch the exception. We had two options: use <code>unsafePerformIO</code> (yuck!) or perform a check before handing the data to the buggy function. We chose the latter, but in some cases, I imagine that could be too difficult to do in order to make it feasible.</p></li></ul><p>The point I’m trying to make is that this is a real problem, and it seems to me that throwing exceptions invalidates one of the primary advantages of Haskell. It disappointed me to realize that a significant amount of code written by FP Complete, one of the primary authors of some of the most important “modern Haskell” code in existence (including Stack), seem to very frequently expose APIs that will throw.</p><p>I’m not sure how much of this stems from a fundamental divide in the Haskell ecosystem and how much it is simply due to Michael Snoyman’s coding style, given that he is the primary author of a number of these tools and libraries that seem very eager to throw exceptions. As just one example of a real situation in which we were surprised by this behavior, we used Snoyman’s http-client library and found that it mysteriously throws upon nearly <em>any</em> failure state:</p><blockquote><p>A note on exceptions: for the most part, all actions that perform I/O should be assumed to throw an <code>HttpException</code> in the event of some problem, and all pure functions will be total. For example, <code>withResponse</code>, <code>httpLbs</code>, and <code>BodyReader</code> can all throw exceptions.</p></blockquote><p>This doesn’t seem entirely unreasonable—after all, isn’t a failure to negotiate TLS fairly catastrophic?—until you consider our use case. We needed to make a subrequest during the extent of another HTTP request to our server, and if that subrequest fails, we absolutely need to handle that failure gracefully. Of course, this is not <em>terrible</em> given that we are in <code>IO</code> so we can actually catch these exceptions, but since this behavior was only noted in a single aside at the top of the documentation, we didn’t realize we were forgetting error handling until far too late and requests were silently failing.</p><p>Exceptions seem to devalue one of the most powerful concepts in Haskell: if I don’t consider all the possibilities, my code <em>does not compile</em>. In practice, when working with APIs that properly encode these possibilities into the type system, this value proposition seems to be real. I really do find myself writing code that works correctly as soon as it compiles. It’s almost magical.</p><p>Using exceptions throws that all out the window, and I wish the Haskell ecosystem was generally more cautious about when to use them.</p><h3><a name="the-string-problem"></a>The String problem</h3><p>I sort of alluded to this a tiny bit in the last section, and that is probably indicative of how bad this issue is. I’m just going to be blunt: <strong>In Haskell, strings suck.</strong></p><p>This is always a bit of an amusing point whenever it is discussed because of how silly it seems. Haskell is a research language with a cutting-edge type system and some of the fanciest features of any language in existence. When everyday programming might use things like “profunctors”, “injective type families”, and “generalized algebraic datatypes”, you would think that dealing with <em>strings</em> would be a well-solved problem.</p><p>But it isn’t. Haskell libraries frequently use not one, not two, but <em><strong>five</strong></em> kinds of strings. Let’s list them off, shall we?</p><ul><li><p>First off, there’s the built-in <code>String</code> type, which is actually an alias for the <code>[Char]</code> type. For those not intimately familiar with Haskell, that’s a <em>linked list of characters</em>. As <a href="http://www.stephendiehl.com/">Stephen Diehl</a> recently put it in <a href="http://www.stephendiehl.com/posts/strings.html">a blog post describing the disaster that is Haskell string types</a>:</p><blockquote><p>This is not only a bad representation, it’s quite possibly the least efficient (non-contrived) representation of text data possible and has horrible performance in both time and space. <em>And it’s used everywhere in Haskell.</em></p></blockquote><p>The point is, it’s really bad. This type is not a useful representation for textual data in practical applications.</p></li><li><p>Moving on, we have a fairly decent type, <code>Text</code>, which comes from <code>Data.Text</code> in the <code>text</code> package. This is a decent representation of text, and it’s probably the one that everything should use. Well, maybe. Because <code>Text</code> comes in two varieties: lazy and strict. Nobody seems to agree on which of those two should be used, though, and they are totally incompatible types: functions that work with one kind of text won’t work with the other. You have to manually convert between them.</p></li><li><p>Finally, we have <code>ByteString</code>, which is horribly misnamed because it really isn’t a string at all, at least not in the textual sense. A better name for this type would have simply been <code>Bytes</code>, which sounds a lot scarier. And that would be good, because data typed as a <code>ByteString</code> is as close as you can get in Haskell to not assigning a type at all: a bytestring holds arbitrary bytes without assigning them any meaning whatsoever!</p><p>Or at least, that’s the intention. The trouble is that people <em>don’t</em> treat bytestrings like that—they just use them to toss pieces of text around, even when those pieces of text have a well-defined encoding and represent textual data. This leads to the <code>decodeUtf8</code> problem mentioned above, but it’s bigger than that because it often ends up with some poor APIs that assign some interpretation to <code>ByteString</code> data without assigning it a different type.</p><p>Again, this is throwing away so much of Haskell’s safety. It would be like using <code>Int</code> to keep track of boolean data (“just use 0 and 1!”) or using empty and singleton lists instead of using <code>Maybe</code>. When you use the precise type, you encode invariants and contracts into statically-checked assertions, but when you use general types like <code>ByteString</code>, you give that up.</p><p>Oh, and did I mention that <code>ByteString</code>s also come in incompatible lazy and strict versions, too?</p></li></ul><p>So, obviously, the answer is to just stop using the bad types and to just use (one kind of) <code>Text</code> everywhere. Great! Except that the other types are totally inescapable. The entire standard library uses <code>String</code> exclusively—after all, <code>text</code> is a separate package—and small libraries often use <code>String</code> instead of <code>text</code> because they have no need to bring in the dependency. Of course, this just means every real application pays the performance hit of converting between all these different kinds of strings.</p><p>Similarly, those that <em>do</em> use <code>Text</code> often use different kinds of text, so code ends up littered with <code>fromStrict</code> or <code>toStrict</code> coercions, which (again) have a cost. I’ve already ranted enough about <code>ByteString</code>, but basically, if you’re using <code>ByteString</code> in your API to pass around data that is semantically text, you are causing me pain. Please stop.</p><p>It seems that the way <code>Data.Text</code> probably <em>should</em> have been designed was by making <code>Text</code> a typeclass, then making the lazy and strict implementations instances of that typeclass. Still, the fact that both of them exist would always cause problems. I’m actually unsure which one is the “correct” choice—I don’t know enough about how the two perform in practice—but it seems likely that picking <em>either</em> one would be a performance improvement over the current system, which is constantly spending time converting between the two.</p><p>This issue has been ranted about plenty, so I won’t ramble on, but if you’re designing new libraries, please, <em>please</em> use <code>Text</code>. Your users will thank you.</p><h3><a name="documentation-is-nearly-worthless"></a>Documentation is nearly worthless</h3><p>Finally, let’s talk about documentation.</p><p>One of my favorite programming languages is Racket. Racket has a documentation tool called Scribble. Scribble is special because it is a totally separate domain-specific language for writing documentation, and it makes it fun and easy to write good explanations. There are even forms for typesetting automatically-rendered examples that look like a REPL. If the examples ever break or become incorrect, the docs don’t even compile.</p><p>All of the Racket core library documentation makes sure to set a good example about what good documentation should look like. The vast majority of the documentation is paragraphs of prose and simple but practical examples. There are also type signatures (in the form of contracts), and those are super important, but they are so effective because of how the prose explains what each function does, when to use it, <em>why</em> you’d use it, and <em>why you wouldn’t use it</em>.</p><p>Everything is cross-referenced automatically. The documentation is completely searchable locally out of the box. As soon as you install a package, its docs are automatically indexed. User-written libraries tend to have pretty good docs, too, because the standard libraries set such a good example <em>and</em> because the tools are so fantastic. Racket docs are really nice, and they’re so good they actually make things like Stack Overflow or even Google mostly irrelevant. It’s all there in the manual.</p><p>Haskell documentation is the opposite of everything I just said.</p><ul><li><p>The core libraries are poorly documented. Most functions include a sentence of description, and almost none include examples. At their worst, the descriptions simply restate the type signature.</p></li><li><p>Third-party libraries’ documentation is even worse, going frequently completely undocumented and actually only including type signatures and nothing else.</p></li><li><p>Haddock is an incredibly user-hostile tool for writing anything other than tiny snippets of documentation and is not very good at supporting prose. Notably, Haddock’s documentation is not generated using Haddock (and it still manages to be almost unusable). Forcing all documentation into inline comments makes users unlikely to write much explanation, and there is no ability for abstraction.</p></li><li><p>Reading documentation locally is very difficult because there is no easy way to open documentation for a particular package in a web browser, and it’s <em>certainly</em> not searchable. This is especially ridiculous given that Hoogle exists, which is one of best ways to search API docs in existence. There should be a <code>stack hoogle</code> command that just opens a Hoogle page for all locally-installed packages and Just Works, but there isn’t.</p></li><li><p>Most valuable information exists outside of documentation, so Google becomes a go-to immediately after a quick glance at the docs, and information is spread across blog posts, mailing lists, and obscure reddit posts.</p></li></ul><p>This is a problem that cannot be fixed by just making Haddock better, nor can it be fixed simply by improving the existing standard library documentation. There is a fundamental problem with Haskell documentation (which, to be completely fair, is not unique to Haskell), which is that its tools do not support anything more than API docs.</p><p>Good documentation is so much more than “here’s what this function does”; it’s about guides and tutorials and case studies and common pitfalls. <a href="http://docs.racket-lang.org/lens/lens-guide.html">This is documentation for someone new to lenses.</a> <a href="https://hackage.haskell.org/package/lens#readme">This is not.</a> Take note of the difference.</p><h2><a name="conclusion-and-other-thoughts"></a>Conclusion and other thoughts</h2><p>Haskell is an incredible programming platform, and indeed, it is sometimes mind-boggling how complete it is. It also has a lot of rough edges, sometimes in places that feel like they need a lot more care, or perhaps they’re even simply unfinished.</p><p>I could spend weeks writing about all the things I really like or dislike about the language, discussing in fine detail all the things that have made me excited or all the little bits that have made me want to tear my hair out. Heck, I could probably spend a month writing about strings alone. That’s not the point, though... I took a risk with Haskell, and it’s paid off. I’m not yet sure exactly how I feel about it, or when I would chose it relative to other tools, but it is currently very high on my list of favorite technologies.</p><p>I did not come to Haskell with a distaste for static typing, despite the fact that I write so much Racket, a dynamically typed language (by default, at least). I don’t really use Typed Racket, and despite my love for Haskell and its type system, I am not sure I will use much more of it than I did before. Haskell and Racket are very different languages, which is justified in some places and probably sort of circumstantial in others.</p><p>The future of Haskell seems bright, and a lot of the changes in the just-released GHC 8 are extremely exciting. I did not list records as a pain point because the changes in GHC 8 appear to make them a <em>lot</em> more palatable, although whether or not they solve that problem completely remains to be seen. I will absolutely continue to write Haskell and push it to its limits where I can, and hopefully try and take as much as I can from it along the way.</p><ol class="footnotes"></ol></article>Simple, safe multimethods in Racket2016-02-18T00:00:00Z2016-02-18T00:00:00ZAlexis King<article><p>Racket ships with <code>racket/generic</code>, a system for defining <em>generic methods</em>, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports <em>single dispatch</em>. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.</p><h2><a name="motivating-multiple-dispatch"></a>Motivating multiple dispatch</h2><p>What is multiple dispatch and why is it necessary? Well, in most cases, it <em>isn’t</em> necessary at all. <a href="http://dl.acm.org/citation.cfm?doid=1449764.1449808">It has been shown that multiple dispatch is much rarer than single dispatch in practice.</a> However, when actually needed, having multiple dispatch in the toolbox is a valuable asset.</p><p>A classic example of multiple dispatch is multiplication over both scalars and vectors. Ideally, all of the following operations should work:</p><pre><code>2 × 3 = 6 +2 × ⟨3, 4⟩ = ⟨6, 8⟩ +⟨3, 4⟩ × 2 = ⟨6, 8⟩ +</code></pre><p>In practice, most languages do not support such flexible dispatch rules without fairly complicated branching constructs to handle each permutation of input types. Furthermore, since most languages only support single dispatch (such as most object-oriented languages), it is nearly impossible to add support for a new combination of types to an existing method.</p><p>To illustrate the above, even if a language supported operator overloading <em>and</em> it included a <code>Vector</code> class that overloaded multiplication to properly work with numbers and vectors, it might not implement matrix multiplication. If a user defines a <code>Matrix</code> class, they may overload <em>its</em> multiplication to support numbers, vectors, and matrices, but it is impossible to extend the multiplication implementation for the <code>Vector</code> class. That method is now completely set in stone, unless it is edited directly (and the programmer may not have access to <code>Vector</code>’s implementation).</p><p>Multiple dispatch solves all of these problems. Rather than specify implementations of functions for singular types, it is possible to specify implementations for sets of types. In the above example, a programmer would be able to define a new function that operates on <code>Vector</code> and <code>Matrix</code> arguments. Since each definition does not “belong” to any given type, extending this set of operations is trivial.</p><h2><a name="multiple-dispatch-in-racket"></a>Multiple dispatch in Racket</h2><p>This blog post is somewhat long and technical, so before proceeding any further, I want to show some real code that actually works so you can get a feel for what I’m talking about. As a proof-of-concept, I have created <a href="https://github.com/lexi-lambda/racket-multimethod">a very simple implementation of multiple dispatch in Racket</a>. The above example would look like this in Racket using my module:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Pardon the somewhat clunky syntax, but the functionality is there. Using the above code works as expected:</p><pre><code>&gt; (mul (num 2) (num 3)) +(num 6) +&gt; (mul (num 2) (vec '(3 4))) +(vec '(6 8)) +&gt; (mul (vec '(3 4)) (num 2)) +(vec '(6 8)) +</code></pre><p>Making the above snippet work is not particularly hard. In fact, it’s likely that most competent Racketeers could do it without much thought. However, there’s a tiny bit more going on behind the scenes than it may seem.</p><h2><a name="the-problem-with-multiple-dispatch"></a>The problem with multiple dispatch</h2><p>The single-dispatch design limitation of <code>racket/generic</code> comes directly from a desire to avoid what has been described as “spooky action at a distance”, a problem that is prevalent in many systems that support methods with multiple dispatch (aka <em>multimethods</em>). Specifically, the issue arises when new method implementations are defined for existing datatypes, which can have far-reaching effects throughout a program because the method table is global state. Both CLOS and Clojure suffer from this shortcoming.</p><p>Interestingly, Haskell with multi-parameter typeclasses (a nonstandard but highly useful extension) makes it quite trivial to create constructs similar to multiple dispatch (though the overload resolution is done at compile-time). The similarities are significant: Haskell <em>also</em> suffers from the possibility of a certain sort of “spooky action”. However, Haskell’s static typing and resolution allows the compiler to catch these potential issues, known as “orphan instances”, at compile time. Even though Racket does not support the same sort of static typing, the same idea can be used to keep multiple dispatch safe using the macro system.</p><h2><a name="safe-dynamically-typed-multiple-dispatch"></a>Safe, dynamically-typed multiple dispatch</h2><p>In order to make multiple dispatch safe, we first need to determine exactly what is unsafe. Haskell has rules for determining what constitutes an “orphan instance”, and these rules are equally applicable for determining dangerous multimethod implementations. Specifically, a definition can be considered unsafe if <em>both</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented was declared in a different module from the implementation.</p></li><li><p><em>All</em> of the types used for dispatch in the multimethod instance were declared in a different module from the implementation.</p></li></ol><p>Conversely, a multimethod implementation is safe if <em>either</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></li><li><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></li></ol><p>Why do these two rules provide a strong enough guarantee to eliminate the dangers created by global state? Well, to understand that, we need to understand what can go wrong if these rules are ignored.</p><h3><a name="multimethods-and-dangerous-instances"></a>Multimethods and dangerous instances</h3><p>What exactly is this dangerous-sounding “spooky action”, and what causes it? Well, the trouble stems from the side-effectful nature of multimethod instance definitions. Consider the Racket module from earlier, which defines multiplication instances for scalars and vectors:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Note that there is not actually a <code>(mul vec vec)</code> implementation. This is intentional: there are <em>two</em> ways to take the product of two vectors, so no default implementation is provided. However, it is possible that another module might desire an instance for <code>mul</code> that takes the dot product, and the programmer might write the following definition:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">foldl</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">y</span><span class="p">)))))</span></code></pre><p>However, there is something fishy about the above definition: it doesn’t need to be exported with <code>provide</code> to work! Since instances don’t create new bindings, they only add dispatch options, they don’t ever need to <code>provide</code> anything. This is problematic, though: it means that a program could continue to happily compile <em>even if</em> the module containing the dot product instance was never loaded with <code>require</code>, but an attempt to multiply two vectors would fail at runtime, claiming that there was no <code>(mul vec vec)</code> implementation. This drastic change of behavior violates Racket programmers’ assumptions about the guarantees made by modules (<code>require</code> should not cause any side-effects if the module’s bindings are not used).</p><p>Of course, while this seems potentially unexpected, it is workable: just be careful to <code>require</code> modules containing instances. Unfortunately, it gets much worse—what if a different library defines <em>its own</em> <code>(mul vec vec)</code> instance? What if that instance takes the cross product instead? That library may function entirely properly on its own, but when loaded alongside the program that defines a dot product instance, it is impossible to determine which instance should be used where. Because <code>define-instance</code> operates by modifying the aforementioned global state, the implementations clash, and the two systems <em>cannot</em> continue to operate together as written.</p><p>This is pretty bad. Defining extra instances is a reasonable use-case for multiple dispatch, but if these instances can break <em>third-party code</em>, how can they be trusted? This sort of problem can make multiple dispatch difficult to reason about and even more difficult to trust.</p><h3><a name="what-determines-safety"></a>What determines safety?</h3><p>With those problems in mind, we can turn back to the two rules for <em>safe</em> multiple dispatch. How do they prevent the above issues? Well, let’s take them one at a time.</p><p>Remember that an instance can be unequivocally determined to be safe if either of the two conditions are true, so we can consider them entirely independently. The first one is simple—an instance is safe if the following condition holds:</p><blockquote><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></blockquote><p>This one is pretty obvious. It is impossible to create a “bad” instance of a method declared in the same module because it is impossible to import the method without also bringing in the instance. Furthermore, a conflicting instance cannot be defined at the place where the types themselves are defined because that would require a circular module dependency, which Racket does not permit.</p><p>With the above explanation in mind, the second condition should make sense, too:</p><blockquote><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></blockquote><p>The same argument for the first point holds for the second, but with the parties swapped. Again, it is impossible to use the instance without somehow requiring the module that defines the datatype itself, so the instance would always be required, anyway. The most interesting aspect of this condition is that it demonstrates that instances can be defined for existing datatypes (that are defined in other modules) just so long as <em>at least one</em> of the datatypes is defined in the same module. This continues to permit the important use-case of extending the interfaces of existing types.</p><h3><a name="encoding-the-safety-rules-into-racket-s-macro-system"></a>Encoding the safety rules into Racket’s macro system</h3><p>In order to keep track of which methods and instances are defined where, I leveraged a technique based on the one <a href="http://www.ccs.neu.edu/racket/pubs/scheme2007-ctf.pdf">used by Typed Racket to keep track of whether or not a typed identifier is used in a typed or untyped context</a>. However, instead of using a simple mutable boolean flag, I used a mutable <a href="http://docs.racket-lang.org/syntax/syntax-helpers.html#%28tech._identifier._set%29">free identifier set</a>, which keeps track of the identifiers within a given module that should be considered “privileged”.</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/id-set</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mark-id-as-privileged!</span> +<span class="w"> </span><span class="n">id-privileged?</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="p">(</span><span class="n">mutable-free-id-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-add!</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-member?</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span></code></pre><p>Making this work with <code>define-generic</code> is obvious: just invoke <code>mark-id-as-privileged!</code> on the method name to note that the method is “privileged” in the scope of the current module. Keeping track of privileged structs is similarly straightforward, though it is a little more devious: the <code>multimethod</code> module provides a custom <code>struct</code> macro that just expands to <code>struct</code> from <code>racket/base</code>, but adds privilege information.</p><p>The <code>define-instance</code> macro does all the heavy lifting to ensure that only privileged identifiers can be used in instance definitions. A simple check for the identifier annotations is performed before proceeding with macro expansion:</p><pre><code class="pygments"><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">types</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">types</span><span class="p">)))</span></code></pre><p>When the privilege checks fail, an error is raised:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">)))</span></code></pre><p>With the above safeguards in place, the dangerous dot product implementation from above <strong>would not be allowed</strong>. The checks manage to encode both of the safety rules into the macro system such that invalid instances will fail <em>at compile time</em>, preventing dangerous uses of multimethods from ever slipping by unnoticed.</p><h3><a name="actually-implementing-multiple-dispatch"></a>Actually implementing multiple dispatch</h3><p>The rest of the multimethod implementation is relatively straightforward and is not even particularly robust. If anything, it is the bare minimum of what would be needed to allow the safety mechanisms above to work. Lots of features that would likely be needed in a real implementation are not included, and graceful error handling is largely ignored.</p><p>Multimethods themselves are implemented as Racket <a href="http://docs.racket-lang.org/guide/proc-macros.html#%28tech._transformer._binding%29">transformer bindings</a> containing custom data, including a reference to the multimethod’s arity and dispatch table. The custom datatype includes a <code>prop:procedure</code> structure type property, which allows such bindings to also function as macros. The macro procedure expands to an operation that looks up the proper instance to use in the multimethod’s dispatch table and invokes it with the supplied arguments.</p><p>The relevant code for defining multimethods is reproduced below:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="n">arity</span><span class="w"> </span><span class="n">dispatch-table</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="n">method</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">method</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="n">args</span><span class="p">))]))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-generic</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method:id</span><span class="w"> </span><span class="n">arg:id</span><span class="w"> </span><span class="n">...+</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">arity</span><span class="w"> </span><span class="p">(</span><span class="nb">length</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">arg</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="nb">make-hash</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod</span><span class="w"> </span><span class="n">arity</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">dispatch-table</span><span class="p">))))]))</span></code></pre><p>The dispatch tables are implemented entirely in terms of Racket’s structure types, so while they can be defined on arbitrary structure types (including ones defined in the Racket standard library), they <em>cannot</em> be defined on primitives such as pairs or vectors. Implementations are registered in the dispatch table using the compile-time information associated with structs’ transformer bindings, and the same information is retrieved from struct instances at runtime to look up the proper implementation to call. Notably, this only works if the struct is <code>#:transparent</code>, or more generally and accurately, if the calling code has access to the struct’s inspector. All structs defined by the <code>struct</code> form from the <code>multimethod</code> module are automatically marked as <code>#:transparent</code>.</p><p>The following code implements defining multimethod instances:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-instance</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="c1">; standard (define (proc ...) ...) shorthand</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">((</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; full (define proc lambda-expr) notation</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="n">proc:expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod-dispatch-table</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">compose1</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="n">extract-struct-info</span><span class="w"> </span><span class="nb">syntax-local-value</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))])</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">struct-types</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">hash-set!</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="n">struct-types</span><span class="w"> </span><span class="n">proc</span><span class="p">))))]))</span></code></pre><p>The resulting implementation is a useful, if certainly incomplete implementation of multimethods in Racket that does not sacrifice the safety provided by <code>racket/generic</code>’s single-dispatch approach.</p><h2><a name="related-work-advantages-and-disadvantages-and-areas-for-future-improvement"></a>Related work, advantages and disadvantages, and areas for future improvement</h2><p>As previously mentioned, this implementation of multiple dispatch was inspired by the types of APIs offered by CLOS and Clojure while also maintaining the safety of <code>racket/generic</code>. The inspiration for the safety rules came from GHC’s detection of orphan instances. Although most of the ideas presented above exist in other places, I am unsure if the concept of safety checking has been used before in any dynamically-typed programming languages.</p><p>The primary advantage offered over Racket’s existing generics system is obvious: multiple dispatch. Furthermore, this system can supersede many uses of <code>racket/generic</code> simply by dispatching on a single type. However, the current implementation does <em>not</em> support all of the features of <code>racket/generic</code>, such as supporting non-structure types and allowing fallback implementations. While those are well within the realm of possibility, other things like attaching structure type properties are probably not possible with this approach, so it is unlikely that the existing system could be subsumed by one like this one.</p><p>Additionally, this implementation would almost certainly need numerous improvements before being useful to most programmers:</p><ul><li><p><strong>Good error reporting for failure cases.</strong> Right now, even something obvious like calling a method on values that do not implement it simply fails with an error produced by <code>hash-ref</code>. In a more interesting sense, using the arity to generate compile-time error messages for <code>define-instance</code> would be a nice improvement.</p></li><li><p><strong>Support for Racket primitive data types.</strong> This might require some cooperation from Racket itself to permit an elegant implementation, but they could also just be special-cased. So long as lookup for primitives was done <em>after</em> consulting the main dispatch table, there wouldn’t be any performance hit for non-primitive types.</p></li><li><p><strong>Option to supply fallback implementations.</strong> This wouldn’t be too hard at all, though it’s questionable whether or not it would be useful without method groupings like <code>define/generic</code> provides. There would likely also need to be some sort of way to check if a set of values implements a particular method.</p></li><li><p><strong>Better cooperation with structure inspectors to alleviate the need for all structures to be transparent.</strong> It’s currently unclear to me how exactly this works and how it <em>should</em> work. There might be a better way to do this without mucking with inspectors.</p></li><li><p><strong>Much more flexible argument lists, including the ability to specify arguments that are not used for dispatch.</strong> This is really a pretty fundamental requirement, but the parsing required was significant enough for me to put it off for this initial prototype.</p></li><li><p><strong>Scribble forms to document generic methods and their instances.</strong> This is something <code>racket/generic</code> <em>doesn’t</em> have, and it has suffered for it. It would be very nice to have easy documentation forms for multimethods.</p></li><li><p><strong>Proper consideration of struct subtyping.</strong> Racket structs support subtyping, which I have not given much thought for this prototype. It is possible that subtyping violates constraints I had assumed would hold, so reviewing the existing code with that context would be useful.</p></li></ul><p>I’m not sure how much effort is involved in most of the above ideas, and in fact I’m not even completely sure how useful this system is to begin with. I have not found myself reaching much for multiple dispatch in my time as a Racket programmer, but that could simply be because it was previously unavailable. It will be interesting to see if that changes now that I have built this system, even if it is a bit rough around the edges.</p><h2><a name="conclusion"></a>Conclusion</h2><p>Despite the lack of need for multiple dispatch to solve most problems, as indicated by its general lack of support in mainstream programming languages, it’s a nice tool to have in the toolbox, and it <em>is</em> asked for in the Racket community from time to time (perhaps due to its familiarity in other parts of the Lisp world). Time will tell if pointing people to something like this will create or stifle interest in multiple dispatch for Racket.</p><p>The source for the <a href="https://github.com/lexi-lambda/racket-multimethod"><code>multimethod</code> package can be found here</a> if you are at all interested in playing with it yourself.</p><ol class="footnotes"></ol></article>ADTs in Typed Racket with macros2015-12-21T00:00:00Z2015-12-21T00:00:00ZAlexis King<article><p>Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate <em>why</em> macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is <strong>ADTs</strong>.</p><h2><a name="warning-this-is-not-a-macro-tutorial"></a>Warning: this is not a macro tutorial</h2><p>First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you <em>are</em> interested in learning about macros, I must recommend Greg Hendershott's <a href="http://www.greghendershott.com/fear-of-macros/">Fear of Macros</a>. It is good. This is not that.</p><p>Now, with that out of the way, let's get started.</p><h2><a name="what-we-re-building"></a>What we&rsquo;re building</h2><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data types</a>, or <em>ADTs</em>, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.</p><p>Racket also already has a facility for creating custom data structures in the form of <em>structs</em>, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.</p><p>With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="kt">Tree</span></code></pre><p>This already demonstrates a few of the core things we'll need to build:</p><ol><li><p>Each ADT has a <em>data type</em>, in this case <code>Tree</code>. This name only exists in the world of types, it isn't a value.</p></li><li><p>Each ADT has various <em>data constructors</em>, in this case <code>Leaf</code> and <code>Node</code>.</p></li><li><p>Each data constructor may accept any number of arguments, each of which have a specific type.</p></li><li><p>The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.</p></li></ol><p>Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>With this in mind, we can add a fifth and final point to our list:</p><ol start="5"><li><p>ADTs must be able to be parametrically polymorphic.</p></li></ol><p>That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.</p><h3><a name="describing-adts-in-racket"></a>Describing ADTs in Racket</h3><p>How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>This looks pretty good. Just like with the Haskell implementation, <code>Tree</code> should only exist at the type level, and <code>Empty</code>, <code>Leaf</code>, and <code>Node</code> should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be <code>(Leaf 7)</code>.</p><p>Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's <code>match</code>[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">tree-sum</span><span class="w"> </span><span class="p">((</span><span class="n">Tree</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">tree</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">tree</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Node</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">l</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">r</span><span class="p">))]))</span></code></pre><p>Given that Racket's <code>struct</code> form automatically produces identifiers that cooperate with <code>match</code>, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.</p><h2><a name="implementing-adts-as-syntax"></a>Implementing ADTs as syntax</h2><p>Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. The <code>syntax/parse</code> library works like the traditional Scheme <code>syntax-case</code> on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.</p><p>Since this is not a macro tutorial, the following implementation assumes you already know how to use <code>syntax/parse</code>. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.</p><h3><a name="parsing-types-with-a-syntax-class"></a>Parsing types with a syntax class</h3><p>To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like <code>Tree</code>, or they can be identifiers with parameters, like <code>(Tree a)</code>. We'll want to cover both cases.</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">))))</span></code></pre><p>This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (<code>...+</code>) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use <code>#:attr</code> to give the <code>param</code> attribute the default value of an empty list, so this syntax class will actually <em>normalize</em> the input we get in addition to actually parsing it.</p><h3><a name="a-first-attempt-at-define-datatype"></a>A first attempt at <code>define-datatype</code></h3><p>Now we can move on to actually implementing <code>define-datatype</code>. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using <code>syntax-parser</code>, which actually does the parsing for our macro.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">]))</span></code></pre><p>This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with <code>_</code> (which will just be the identifier <code>define-datatype</code>), then expecting a <code>type-name</code>, which uses the <code>type</code> syntax class we defined above. Next, we expect zero or more <code>data-constructor</code>s, which also use the <code>type</code> syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.</p><p>Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">([</span><span class="n">f</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))]))</span></code></pre><p>This is actually really close to being correct. This will generate a struct definition for each <code>data-constructor</code>, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have <em>names</em>, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for <em>all</em> the fields, <code>f</code>, so if any data constructor has two or more fields, we'll get an error.</p><p>Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called <code>generate-temporary</code>, which generates random identifiers. Our next attempt might look like this:</p><pre><code class="pygments"><span class="o">#`</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="o">#,</span><span class="p">(</span><span class="n">generate-temporary</span><span class="p">)</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>The <code>#,</code> lets us "escape" from the template to execute <code>(generate-temporary)</code> and interpolate its result into the syntax. Unfortunately, this doesn't work. We <em>do</em> generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.</p><h3><a name="more-leveraging-syntax-classes"></a>More leveraging syntax classes</h3><p>As it turns out, this is <em>also</em> easy to do with syntax classes. We can add an extra attribute to our <code>type</code> syntax class to generate a random identifier with each one. Again, we can use <code>#:attr</code> to do that automatically. Our new definition for <code>type</code> will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span></code></pre><p>Here we're using <code>generate-temporaries</code> instead of <code>generate-temporary</code>, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each <code>param</code>.</p><p>We can now fix our macro to use this <code>field-id</code> attribute instead of the static field name:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><h3><a name="creating-the-supertype"></a>Creating the supertype</h3><p>We're almost done—now we just need to implement our overall type, the one defined by <code>type-name</code>. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">Tree</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">Leaf</span><span class="w"> </span><span class="n">Node</span><span class="p">))</span></code></pre><p>However, a polymorphic type alias would need to include the type parameters in each subtype, like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using <strong>procedural macros</strong>.</p><p>To build each properly-instantiated type, we'll use a combination of <code>define/with-syntax</code> and Racket's list comprehensions, <code>for/list</code>. The <code>define/with-syntax</code> form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by <code>syntax-parser</code>. This will allow us to break up our result into multiple steps. Technically, <code>define/with-syntax</code> is not strictly necessary—we could just use <code>#`</code> and <code>#,</code>—but it's cleaner to work with.</p><p>We'll start by defining a set of instantiated data constructor types, one per <code>data-constructor</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span></code></pre><p>Now with our definition for <code>data-type</code>, we can implement our type alias for the supertype extremely easily:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><h3><a name="putting-it-all-together"></a>Putting it all together</h3><p>There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by <code>type-name</code> are in scope for each data constructor's structure definition. We can do this by making use of <code>type-name.param</code> within each produced struct definition, resulting in this:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><p>And we're done! The final macro, now completed, looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span> + +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span><span class="w"> </span><span class="k">...</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">)))]))</span></code></pre><p>It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.</p><h2><a name="using-our-adts"></a>Using our ADTs</h2><p>With the macro built, we can now actually use our ADTs using the syntax we described! The following is now <em>valid code</em>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span> +<span class="nb">-</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">Positive-Byte</span><span class="p">)</span> +<span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span></code></pre><p>We can use this to define common data types, such as Haskell's <code>Maybe</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Nothing</span><span class="p">)</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-default</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-default</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-then</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-then</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Nothing</span><span class="p">)]))</span></code></pre><p>And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="n">Expr</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="n">Number</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Expr</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Number</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">e</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Value</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Add</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">-</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Divide</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">/</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">7</span><span class="p">))))</span> +<span class="mi">4</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, <a href="https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e">I've put together a gist here</a>.</p><h2><a name="conclusions-and-credit"></a>Conclusions and credit</h2><p>This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly <em>are</em> powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.</p><p>This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.</p><p>That said, I think it's pretty cool.</p><p>Finally, I must give credit where credit is due. Thanks to <a href="http://andmkent.com">Andrew M. Kent</a> for the creation of the <a href="https://github.com/andmkent/datatype">datatype</a> package, which served as the inspiration for this blog post. Many thanks to <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> and <a href="http://www.ccs.neu.edu/home/matthias/">Matthias Felleisen</a> for their work on creating <code>syntax/parse</code>, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.</p><p>Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.</p><ol class="footnotes"></ol></article>Functionally updating record types in Elm2015-11-06T00:00:00Z2015-11-06T00:00:00ZAlexis King<article><p><a href="http://elm-lang.org">Elm</a> is a wonderful language for building web apps, and I love so much of its approach to language design. Elm does so many things <em>right</em> straight out of the box, and that's a real breath of fresh air in the intersection of functional programming and web development. Still, it gets one thing wrong, and unfortunately, that one thing is incredibly important. Elm took the "functions" out of "functional record types".</p><p>Almost any software program, at its core, is all about data. Maybe it's about computing data, maybe it's about manipulating data, or maybe it's about displaying data, but at the end of the day, some sort of data model is going to be needed. The functional model is a breathtakingly elegant system for handling data and shuttling it around throughout a program, and <a href="https://en.wikipedia.org/wiki/Functional_reactive_programming">functional reactive programming</a>, which Elm uses to model event-like interactions, makes this model work even better. The really important thing, though, is what tools Elm actually gives you to model your data.</p><h2><a name="a-brief-primer-on-elm-records"></a>A brief primer on Elm records</h2><p>Elm supports all the core datatypes one would expect—numbers, strings, booleans, optionals, etc.—and it allows users to define their own types with ADTs. However, Elm also provides another datatype, which it calls "records". Records are similar to objects in JavaScript: they're effectively key-value mappings. They're cool data structures, and they work well. Here's an example of creating a <code>Point</code> datatype in Elm:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">alias</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Float</span><span class="p">,</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Float</span><span class="w"> </span><span class="p">}</span></code></pre><p>Notice that <code>Point</code> is declared as a type <em>alias</em>, not as a separate type like an ADT. This is because record types are truly encoded in the type system as values with named fields, not as disparate types. This allows for some fun tricks, but that's outside the scope of this blog post.</p><h2><a name="the-good"></a>The good</h2><p>What I'd like to discuss is what it looks like to <em>manipulate</em> these data structures. Constructing them is completely painless, and reading from them is super simple. This is where the record system gets everything very <em>right</em>.</p><pre><code class="pygments"><span class="nv">origin</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">origin</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">distanceBetween</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span> +<span class="nv">distanceBetween</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nv">b</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="nv">dx</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">a</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="nv">b</span><span class="nf">.</span><span class="nv">x</span> +<span class="w"> </span><span class="nv">dy</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">a</span><span class="nf">.</span><span class="nv">y</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="nv">b</span><span class="nf">.</span><span class="nv">y</span> +<span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="nv">sqrt</span><span class="w"> </span><span class="p">(</span><span class="nv">dx</span><span class="nf">*</span><span class="nv">dx</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="nv">dy</span><span class="nf">*</span><span class="nv">dy</span><span class="p">)</span></code></pre><p>The syntax is clean and simple. Most importantly, however, the record system is functional (in the "functional programming" sense). In a functional system, it's useful to express concepts in terms of function composition, and this is very easy to do in Elm. Creating a function to access a field would normally be clunky if you always needed to do <code>record.field</code> to access the value. Fortunately, Elm provides some sugar:</p><pre><code class="pygments"><span class="c1">-- These two expressions are equivalent:</span> +<span class="p">(</span><span class="nf">\</span><span class="nv">record</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">record</span><span class="nf">.</span><span class="nv">field</span><span class="p">)</span> +<span class="nf">.</span><span class="nv">field</span></code></pre><p>Using the <code>.field</code> shorthand allows writing some other functions in terms of composition, as most functional programmers would desire:</p><pre><code class="pygments"><span class="nv">doubledX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span> +<span class="nv">doubledX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">(</span><span class="nf">(*)</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="nf">&lt;&lt;</span><span class="w"> </span><span class="nf">.</span><span class="nv">x</span></code></pre><p>This satisfies me.</p><h2><a name="the-bad"></a>The bad</h2><p>So if everything in Elm is so great, what am I complaining about? Well, while the syntax to access fields is convenient, the syntax to <em>functionally set</em> fields is questionably clunky. Consider a function that accepts a point and returns a new point with its <code>x</code> field set to <code>0</code>:</p><pre><code class="pygments"><span class="nv">zeroedX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">zeroedX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}</span></code></pre><p>This doesn't look too bad, does it? It's clear and concise. To me, though, there's something deeply wrong here... this function has a lot of redundancy! It seems to me like we should be able to write this function more clearly in a point-free style. The <code>.field</code> shorthand "functionalizes" the record getter syntax, so there must be a function version of the update syntax, right? Maybe it would look something like this:</p><pre><code class="pygments"><span class="nv">zeroedX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">zeroedX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="err">!</span><span class="nv">x</span><span class="w"> </span><span class="mi">0</span></code></pre><p>But alas, there is no such syntax.</p><p>Now you may ask... why does it matter? This seems trivial, and in fact, the explicit updater syntax may actually be more readable by virtue of how explicit it is. You'd be right, because so far, these examples have been horribly contrived. But let's consider a slightly more useful example: <em>functionally updating</em> a record.</p><p>What's the difference? Well, say I wanted to take a point and increment its <code>x</code> field by one. Well, I can easily write a function for that:</p><pre><code class="pygments"><span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span></code></pre><p>Not terrible, though a <em>little</em> verbose. Still, what if we want to also add a function that <em>decrements</em> <code>x</code>?</p><pre><code class="pygments"><span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span></code></pre><p>Oh, gosh. That's basically the exact same definition but with the operation flipped. Plus we probably want these operations for <code>y</code>, too. Fortunately, there's an easy solution: just pass a function in to <em>transform</em> the value! We can define an <code>updateX</code> function that allows us to do that easily, then we can define our derived operations in terms of that:</p><pre><code class="pygments"><span class="nv">updateX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateX</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>Not only is that much cleaner, but we can now use it to implement all sorts of other operations that allow us to add, subtract, multiply, or divide the <code>x</code> field. Now we just need to generalize our solution to work with the <code>x</code> <em>and</em> <code>y</code> fields!</p><p>Oh, wait. <strong>We can't.</strong></p><h2><a name="the-ugly"></a>The ugly</h2><p>This is where everything breaks down completely. Elm does not offer enough abstraction to reduce this level of crazy duplication:</p><pre><code class="pygments"><span class="nv">updateX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateX</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">updateY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateY</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">y</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementY</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateY</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementY</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>We sure can give it a shot, though. At the very least, we <em>can</em> implement the increment and decrement functions in a more general way by passing in an updater function:</p><pre><code class="pygments"><span class="nv">increment</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">((</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span> +<span class="nv">increment</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>Now, with <code>updateX</code> and <code>updateY</code>, we can increment either field very clearly and expressively. If we shorten the names to <code>uX</code> and <code>uY</code>, then the resulting code is actually very readable:</p><pre><code class="pygments"><span class="nv">pointAbove</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">uY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="nv">pointBelow</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">uY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>It's almost like English now: "update Y using this transformation". This is actually pretty satisfactory. The trouble arises when you have a struct with many fields:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">alias</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">strength</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">charisma</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">intellect</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="c1">-- etc.</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>It might be very convenient to have generic functional updaters in this case. One could imagine a game that has <code>Potion</code> items:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Potion</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="kt">Potion</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="p">(</span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="p">)</span></code></pre><p>And then some different kinds of potions:</p><pre><code class="pygments"><span class="nv">potions</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Health Potion"</span><span class="w"> </span><span class="p">(</span><span class="nv">uHealth</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">))),</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Greater Intellect Potion"</span><span class="w"> </span><span class="p">(</span><span class="nv">uIntellect</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Potion of Weakness"</span><span class="w"> </span><span class="p">(</span><span class="nv">uStrength</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">//</span><span class="w"> </span><span class="mi">5</span><span class="p">)))</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is a really elegant way to think about items that can affect a player's stats! Unfortunately, it also means you have to define updater functions for <em>every single field in the record</em>. This can get tedious rather quickly:</p><pre><code class="pygments"><span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uStrength</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uStrength</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">strength</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">strength</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uCharisma</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uCharisma</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">charisma</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">charisma</span><span class="w"> </span><span class="p">}</span> + +<span class="c1">-- etc.</span></code></pre><p>This is pretty icky. Could there be a better way?</p><h2><a name="trying-to-create-a-more-general-abstraction"></a>Trying to create a more general abstraction</h2><p>Interestingly, this pattern doesn't <em>need</em> to be this bad. There are better ways to do this. Let's revisit our updater functions.</p><p>Really, <code>update</code> can be defined in terms of two other primitive operations: a read and a (functional) write. What would it look like if we implemented it that way instead of requiring special updater functions to be defined? Well, it would look like this:</p><pre><code class="pygments"><span class="nv">update</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="nv">b</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="nv">b</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span> +<span class="nv">update</span><span class="w"> </span><span class="nv">get</span><span class="w"> </span><span class="nv">set</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">set</span><span class="w"> </span><span class="p">(</span><span class="nv">f</span><span class="w"> </span><span class="p">(</span><span class="nv">get</span><span class="w"> </span><span class="nv">x</span><span class="p">))</span><span class="w"> </span><span class="nv">x</span></code></pre><p>The type definition is a little long, but it's really pretty simple. We just supply a getter and a setter, then a function to do the transformation, and finally a record to actually transform. Of course, as you can see from the type, this function isn't actually specific to records: it can be used with any value for which a getter and setter can be provided.</p><p>The trouble here is that writing field setters isn't any easier in Elm than writing field updaters. They still look pretty verbose:</p><pre><code class="pygments"><span class="nv">sHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">sHealth</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="nv">sHealth</span></code></pre><p>So, at the end of it all, this isn't really a better abstraction. Still remember my fantasy <code>!field</code> setter shorthand half a blog post ago? Now perhaps it makes a little more sense. <em>If</em> such a syntax existed, then defining the updater would be incredibly simple:</p><pre><code class="pygments"><span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="err">!</span><span class="nv">health</span></code></pre><p>Still, no syntax, no easy updaters, and by extension, no easy, declarative description of behavior without quite a bit of boilerplate.</p><h2><a name="conclusions-and-related-work"></a>Conclusions and related work</h2><p>Elm is a very promising language, and it seems to be in fairly rapid development. So far, its author, <a href="https://twitter.com/czaplic">Evan Czaplicki</a>, has taken a very cautious approach to implementing language features, especially potentially redundant ones. This caution is why things like operator slicing, "where" clauses, and special updater syntax have not yet made it into the language. Maybe at some point these will be deemed important enough to include, but for the time being, they've been excluded.</p><p>I obviously think that having this sort of thing is incredibly important to being able to write expressive code without a huge amount of overhead. However, I also do <em>not</em> want to give the impression that I think adding special setter syntax is the only way to do it.</p><p>Seasoned functional programmers will surely have noticed that many of these concepts sound a lot like lenses, and Elm actually already has a lens-like library authored by Evan himself, called <a href="https://github.com/evancz/focus">Focus</a>. This, however, does not actually solve the problem: it requires manual description of setters just like the purely function based approach does. Really, lenses are just the logical next step in the line of abstraction I've already laid out above.</p><p>Interestingly, PureScript and Elm, the two Haskell-likes-on-the-frontend that I've paid attention to (though PureScript is much closer to Haskell than Elm), both have this very same problem. Haskell itself solves it with macros via Template Haskell. My favorite language, Racket, solves it with its own macro system. Is there another way to do these things that doesn't involve introducing a heavyweight macro system? Definitely. But I think this is a <em>necessary feature</em>, not a "nice to have", so if a macro system is out of the picture, then a simpler, less flexible solution is the obvious logical alternative.</p><p>I really like Elm, and most of my experiences with it have been more than enough to convince me that it is a fantastic language for the job. Unfortunately, the issue of functional record updaters has been quite the frustrating obstacle in my otherwise frictionless ride. I will continue to happily use Elm over other, far less accommodating tools, but I hope that issues like these will be smoothed out as the language and its ecosystem matures.</p><ol class="footnotes"></ol></article>Canonical factories for testing with factory_girl_api2015-09-23T00:00:00Z2015-09-23T00:00:00ZAlexis King<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article>Managing application configuration with Envy2015-08-30T00:00:00Z2015-08-30T00:00:00ZAlexis King<article><p>Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, <a href="http://12factor.net">The Twelve-Factor App</a> provides a set of standards for keeping web apps sane, and <a href="http://12factor.net/config">one of those guidelines advises keeping configuration in the environment</a>.</p><p><a href="https://github.com/lexi-lambda/envy">Envy</a> is the declarative bridge between Racket code and the outside world of the environment.</p><h2><a name="introducing-envy"></a>Introducing Envy</h2><p>I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require <code>envy</code> and you're good to go.</p><p>The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:</p><pre><code class="pygments"><span class="c1">; environment.rkt</span> +<span class="kn">#lang </span><span class="nn">typed/racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">envy</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/provide-environment</span> +<span class="w"> </span><span class="n">api-token</span> +<span class="w"> </span><span class="p">[</span><span class="n">log-level</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="kd">#:default</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">info</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">parallel?</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Boolean</span><span class="p">])</span></code></pre><p>When this module is required, Envy will automatically do the following:</p><ol><li><p>Envy will check the values of three environment variables: <code>API_TOKEN</code>, <code>LOG_LEVEL</code>, and <code>PARALLEL</code>.</p></li><li><p>If either <code>API_TOKEN</code> or <code>PARALLEL</code> is not set, an error will be raised:</p><pre><code>envy: The required environment variable "API_TOKEN" is not defined. +</code></pre></li><li><p>The values for <code>LOG_LEVEL</code> and <code>PARALLEL</code> will be parsed to match their type annotations.</p></li><li><p>If <code>LOG_LEVEL</code> is not set, it will use the default value, <code>'info</code>.</p></li><li><p>The values will be stored in <code>api-token</code>, <code>log-level</code>, and <code>parallel?</code>, all of which will be provided by the enclosing module.</p></li></ol><p>Now just <code>(require (prefix-in env: "environment.rkt"))</code>, and the environment variables are guaranteed to be available in your application's code.</p><h2><a name="working-with-typed-racket"></a>Working with Typed Racket</h2><p>As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, <code>define/provide-environment</code> will <em>only</em> work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.</p><p>However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.</p><pre><code>&gt; parallel? +- : Boolean +#t +</code></pre><p>Envy really shines when using optional environment variables with the <code>#:default</code> option. The type of the value given to <code>#:default</code> doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.</p><pre><code>&gt; (define-environment + [num-threads : Positive-Integer #:default #f]) +&gt; num-threads +- : (U Positive-Integer #f) +#f +</code></pre><p>This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.</p><h2><a name="and-more"></a>And more...</h2><p>To see the full set of features that Envy already provides, <a href="https://lexi-lambda.github.io/envy/envy.html">take a look at the documentation</a>. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, <a href="https://github.com/lexi-lambda/envy/issues">open an issue and make a suggestion</a>! I already have plans for a <code>#lang envy</code> DSL, which will hopefully cut the boilerplate out in its entirety.</p><p>And finally, to give credit where credit is due, Envy is heavily inspired by <a href="https://github.com/eval/envied">Envied</a> (both in name and function), an environment variable manager for Ruby, which I've used to great effect.</p><p>Try it out!</p><ul><li><p><code>raco pkg install envy</code></p></li><li><p><a href="https://github.com/lexi-lambda/envy">Envy on GitHub</a></p></li><li><p><a href="https://lexi-lambda.github.io/envy/envy.html">Envy documentation</a></p></li></ul><ol class="footnotes"></ol></article>Deploying Racket applications on Heroku2015-08-22T00:00:00Z2015-08-22T00:00:00ZAlexis King<article><p><a href="https://www.heroku.com">Heroku</a> is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.</p><h2><a name="building-the-server"></a>Building the server</h2><p>Racket's <a href="http://docs.racket-lang.org/web-server/index.html">web-server</a> package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">web-server/servlet</span> +<span class="w"> </span><span class="n">web-server/servlet-env</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="n">req</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">response/xexpr</span> +<span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="ss">html</span><span class="w"> </span><span class="p">(</span><span class="ss">head</span><span class="w"> </span><span class="p">(</span><span class="ss">title</span><span class="w"> </span><span class="s2">"Racket Heroku App"</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="ss">body</span><span class="w"> </span><span class="p">(</span><span class="ss">h1</span><span class="w"> </span><span class="s2">"It works!"</span><span class="p">)))))</span> + +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span></code></pre><p>Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the <code>PORT</code> environment variable. We can access this using the Racket <code>getenv</code>[racket] function.</p><p>Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass <code>#f</code> for the <code>#:listen-ip</code> argument.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;number</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">))</span> +<span class="w"> </span><span class="mi">8080</span><span class="p">))</span> +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span><span class="p">)</span></code></pre><p>Also, by default, <code>serve/servlet</code>[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.</p><pre><code class="pygments"><span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span> +<span class="w"> </span><span class="kd">#:command-line?</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span></code></pre><p>That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.</p><h2><a name="setting-up-our-app-for-heroku"></a>Setting up our app for Heroku</h2><p>The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:</p><pre><code class="pygments">$<span class="w"> </span>git<span class="w"> </span>init +$<span class="w"> </span>heroku<span class="w"> </span>git:remote<span class="w"> </span>-a<span class="w"> </span>racket-heroku-sample +$<span class="w"> </span>heroku<span class="w"> </span>buildpacks:set<span class="w"> </span>https://github.com/lexi-lambda/heroku-buildpack-racket</code></pre><p>We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the <code>RACKET_VERSION</code> environment variable as follows:</p><pre><code class="pygments">$<span class="w"> </span>heroku<span class="w"> </span>config:set<span class="w"> </span><span class="nv">RACKET_VERSION</span><span class="o">=</span><span class="m">6</span>.2.1</code></pre><p>Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.</p><p>Specifically, we just want to run our <code>serve.rkt</code> module. The Racket buildpack installs the repository as a package, so we can run <code>racket</code> with the <code>-l</code> flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:</p><pre><code>web: racket -l sample-heroku-app/server +</code></pre><p>Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can <a href="https://racket-heroku-sample.herokuapp.com">navigate to our app's URL and actually see it running live</a>!</p><h2><a name="conclusion"></a>Conclusion</h2><p>That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.</p><p>That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can <a href="https://github.com/lexi-lambda/racket-sample-heroku-app">find it on GitHub here</a>. For more details on the buildpack itself, <a href="https://github.com/lexi-lambda/heroku-buildpack-racket">it's also available on GitHub here</a>.</p><ol class="footnotes"></ol></article>Automatically deploying a Frog-powered blog to GitHub pages2015-07-18T00:00:00Z2015-07-18T00:00:00ZAlexis King<article><p>So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses <a href="http://www.greghendershott.com">Greg Hendershott</a>'s fantastic <a href="https://github.com/greghendershott/frog">Frog</a> tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via <a href="https://travis-ci.org">Travis CI</a>, so my blog is always up-to-date.</p><h2><a name="setting-up-frog"></a>Setting up Frog</h2><p>I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple <code>raco pkg install frog</code> followed by <code>raco frog --init</code> and <code>raco frog -bp</code> created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is <code>raco frog -n "Post Title"</code>, and you're good to go.</p><p>By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use <a href="http://sass-lang.com">Sass</a> for my stylesheets, potentially with support for <a href="http://coffeescript.org">CoffeeScript</a> later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used <a href="http://gulpjs.com">Gulp</a> in conjunction with <a href="https://www.npmjs.com">NPM</a> for build and dependency management.</p><p>Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.</p><h2><a name="configuring-automatic-deployment-with-travis"></a>Configuring automatic deployment with Travis</h2><p>Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being <a href="https://gist.github.com/domenic/ec8b0fc8ab45f39403dd">this Gist</a>, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special <code>gh-pages</code> branch.</p><p>To make this easy, Frog can be configured to output to a separate directory via the <code>.frogrc</code> configuration file. I chose to output to the <code>out</code> directory:</p><pre><code>output-dir = out +</code></pre><p>I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.</p><pre><code>$ cd out +$ git init +$ git add . +$ git commit -m "Deploy to GitHub Pages" +$ git push --force "$REMOTE_URL" master:gh-pages +</code></pre><p>The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's <a href="http://docs.travis-ci.com/user/encryption-keys/">encryption keys</a> along with a GitHub <a href="https://github.com/settings/tokens">personal access token</a>. Just install the Travis CLI client, copy the access token, and run a command:</p><pre><code>$ gem install travis +$ travis encrypt GH_TOKEN=&lt;access token...&gt; +</code></pre><p>The output of that command is an encrypted value to be placed in an environment variable in the project's <code>.travis.yml</code> configuration file. The URL for the repository on GitHub will also need to be specified as well:</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span></code></pre><p>Now all that's left is configuring the <code>.travis.yml</code> to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to <code>python</code>, then installed Racket and Frog as pre-installation steps.</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span></code></pre><p>(It might be worth noting that Greg Hendershott <em>also</em> maintains the repository that contains the above Travis build script!)</p><p>Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses <code>master</code>, not <code>gh-pages</code>. Obviously, I didn't want Travis running on my <code>master</code> branch, since it would be deploying to that, so I added a branch whitelist:</p><pre><code class="pygments"><span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span></code></pre><p>All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:</p><pre><code class="pygments"><span class="ch">#!/bin/bash</span> +<span class="nb">set</span><span class="w"> </span>-ev<span class="w"> </span><span class="c1"># exit with nonzero exit code if anything fails</span> + +<span class="c1"># clear the output directory</span> +rm<span class="w"> </span>-rf<span class="w"> </span>out<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">0</span><span class="p">;</span> + +<span class="c1"># build the blog files + install pygments for highlighting support</span> +npm<span class="w"> </span>install +npm<span class="w"> </span>run<span class="w"> </span>build +pip<span class="w"> </span>install<span class="w"> </span>pygments +raco<span class="w"> </span>frog<span class="w"> </span>--build + +<span class="c1"># go to the out directory and create a *new* Git repo</span> +<span class="nb">cd</span><span class="w"> </span>out +git<span class="w"> </span>init + +<span class="c1"># inside this git repo we&#39;ll pretend to be a new user</span> +git<span class="w"> </span>config<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Travis CI"</span> +git<span class="w"> </span>config<span class="w"> </span>user.email<span class="w"> </span><span class="s2">"&lt;your@email.here&gt;"</span> + +<span class="c1"># The first and only commit to this new Git repo contains all the</span> +<span class="c1"># files present with the commit message "Deploy to GitHub Pages".</span> +git<span class="w"> </span>add<span class="w"> </span>. +git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"Deploy to GitHub Pages"</span> + +<span class="c1"># Force push from the current repo&#39;s master branch to the remote</span> +<span class="c1"># repo. (All previous history on the branch will be lost, since we are</span> +<span class="c1"># overwriting it.) We redirect any output to /dev/null to hide any sensitive</span> +<span class="c1"># credential data that might otherwise be exposed.</span> +git<span class="w"> </span>push<span class="w"> </span>--force<span class="w"> </span>--quiet<span class="w"> </span><span class="s2">"https://</span><span class="si">${</span><span class="nv">GH_TOKEN</span><span class="si">}</span><span class="s2">@</span><span class="si">${</span><span class="nv">GH_REF</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>master<span class="w"> </span>&gt;<span class="w"> </span>/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span></code></pre><p>For reference, my final <code>.travis.yml</code> looked like this:</p><pre><code class="pygments"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span> +<span class="nt">python</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&#39;3.4&#39;</span> + +<span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span> + +<span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/lexi-lambda/lexi-lambda.github.io.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;long secure token...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span> + +<span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bash ./deploy.sh</span></code></pre><p>That's it! Now I have a working blog that I can publish just by pushing to the <code>source</code> branch on GitHub.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/all.rss.xml b/feeds/all.rss.xml new file mode 100644 index 0000000..49677b8 --- /dev/null +++ b/feeds/all.rss.xml @@ -0,0 +1,3172 @@ +Alexis King’s BlogAlexis King’s Bloghttps://lexi-lambda.github.io/29 May 202529 May 202560A break from programming languageshttps://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/https://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/29 May 2025<article><p>This is a blog post I have been considering writing for a long time.</p><p>People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.</p><p>My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.</p><p>This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for <em>me</em>, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why <em>not</em>—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.</p><h2><a name="whence-programming-languages"></a>Whence programming languages</h2><p>I have had a personal fascination with programming languages for as long as I have been using them. I made my first serious forays into programming in the fifth grade, writing (very simple and mostly very bad) Flash games in ActionScript 3. I wrote my first programming language interpreter in my sophomore year of high school, a dynamically-typed, object-oriented Lisp with prototypal inheritance. The interpreter was written in C, and despite it being my first serious endeavor with the language, I used preprocessor macros to implement a form of exceptions in terms of <code>setjmp.h</code>. It is an amusing project in retrospect, given what my design sensibilities would eventually turn out to be, but it at least provides a little insight into how much the subject was already on my mind in my teenage years.</p><p>I have always deeply enjoyed programming. That is no less true now than it was fifteen years ago. As someone with (then untreated) ADHD, the automation of repetitive tasks has always held immediate appeal, but it did not take long to discover I enjoy programming for its own sake. It is inherently delightful to me to direct an infinitely reconfigurable machine towards any end I desire, given only a little thought and some time at a keyboard. Program <em>design</em>, even in the large, was something that immediately appealed to me. It is often as much a challenge of organizing one’s thoughts as it is a matter of translating them to a form the computer can understand, and the former is a challenge I have always found rewarding.</p><p>Even so, as every programmer inevitably discovers, the gap between one’s thoughts and executable code is never so narrow as we would like. I quickly discovered that a distressingly large amount of my time was being spent on the exactly the sort of repetitive task I had hoped to avoid, mechanically typing out reams of boilerplate in order to carry out what seemed to be incredibly simple tasks.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> It felt strange that we, as programmers, could be one of the lucky disciplines equipped with the means to produce our own tools, yet the tools we work with are so frequently unsatisfactory.</p><p>Programming is an astonishingly young discipline. FORTRAN, arguably the first recognizable programming language, is less than seventy years old, and the first stored-program computers are only a decade older. Programmers have been trying to bridge the gap between our conceptualizations of programs and the (often laborious) act of programming for as long as computing has existed. As John Backus said in 1979,</p><blockquote><p>Much of my work has come from being lazy. I didn’t like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs.</p></blockquote><p>In my experience, this sentiment perfectly captures the strange relationship many programming language enthusiasts have with our craft. We enjoy programming so much that we are willing to spend enormous time, thought, and effort working on programming systems precisely to free ourselves from the tortuous burden of writing programs.</p><p>One possible means of reconciling this apparent contradiction would be to hypothesize that programmers like me or like Backus enjoy the <em>results</em> of our programs but not the process of writing them. We want to get things done, and we view the computer as a useful but necessary evil that should get out of the way as much and as expediently as possible. However, although I am sure there are many programmers who do match that description, I do not count myself among them, and I would likewise find it extremely difficult to believe that Backus—a man who dedicated his life to designing ever more sophisticated means of expressing computer programs—was someone fundamentally uninterested in programming.</p><p>On the contrary, I see a great deal of work on programming languages as a struggle—often in vain—by those of us who love programming so much that we wish to free it of its unfortunate imperfections. We strive to remove the impediments and obstacles we face when translating the beautiful, platonic programs we hold in our heads into the comparatively cold, alien language of silicon calculating machines. There is a widespread mythology that programming languages are primarily abstractions over machine code, but this perspective has been misguided for almost as long as programming languages have existed. Programming languages are instruments of thought, frameworks for precisely specifying an executable idea, and they are designed almost entirely for <em>humans</em>, with computers often providing little more than a loose set of inconvenient restrictions.</p><h3><a name="to-compute-is-human-to-program-divine"></a>To compute is human, to program divine</h3><p>The earliest computers were developed with a singular purpose in mind: to compute. A program was, essentially, a sequence of instructions containing the computations to be performed. This conceptualization of a computer program as essentially analogous to a cooking recipe or assembly manual has remained curiously persistent, even to the present day.</p><p>Almost no modern programs of any complexity look anything like a cooking recipe. Even programs written in the most imperative programming languages under the sun are complex webs of logic specifying dataflow, access policy, and interaction patterns between semi-autonomous, reactive subsystems, many of which come equipped with their own living, beating hearts. At the micro scale, it is true that programs specify behavior as a series of steps to be performed, but even this is a fiction maintained by modern compilers for humans’ convenience. Through a compiler’s eyes, the <em>real</em> programs are whatever is left after the comforting fiction is boiled away to obtain a homogenous pile of SSA and dataflow graphs to be haphazardly reassembled into a patchwork of basic blocks in a language most programmers would not even begin to understand.</p><p>Indeed, in stark contrast to the mental model of the programmer as a sort of silicon whisperer, I believe most practicing software engineers are primarily maintaining vast behavioral specifications whose defining sense of rigor comes not from computers <em>per se</em> but from the consistency we demand from their results. In other words, we are trained to be fluent speakers of languages that allow us to articulate a solution to a problem with a level of precision that ensures every legitimate interpretation is and will always be to our satisfaction. Crucially, these specifications do not <em>fully</em> specify how the task is to be carried out. Optimizing compilers are free to make their own choices with whatever leeway we permit them. HTML/CSS user agents may interpret our instructions in whatever way preserves whatever web standards happen to require. Even RDBMS query planners, a foundational technology largely unchanged for the past several decades, are essentially expert systems designed to decide on the fly how best to execute searches and retrievals under ever-changing conditions.</p><p>This is not to say that the task of programming is fully removed from the machine that ultimately executes our instructions, nor is it even really a suggestion it ought to be. Performance and debugging mandate a certain familiarity with the underlying system, even if the full complexities of its true inner workings remain carefully hidden from even its expert users. My point is simply that programs are not rote algorithms, and the act of programming is not primarily characterized by algorithmic thinking. Rather, programming is fundamentally a game of domain modeling and system design, with bits of algorithmic logic inserted here and there to glue the whole thing together.</p><p>My interest in pushing the frontier of programming language design has always been and continues to be an interest in making the task of writing programs feel closer to the task of designing them. In an ideal world, I would have a language expressive enough to more or less directly translate the computer programs I have in my head (which, for me, more often than not take <em>visual</em> form) into a digitized structure a computer may faithfully execute. It is that task I found myself fixated on at the tender age of fifteen, and it is one I would remain fixated on for the better part of the following decade.</p><h2><a name="ten-years-of-programming-languages"></a>Ten years of programming languages</h2><p>I turned twenty eight this year. Almost exactly ten years ago, I started my first professional software engineering job. It was a fairly mundane position writing Ruby on Rails and JavaScript, but even then, I spent a great deal of free time pursuing the programming language hobby projects that truly caught my fancy. My first professional experience working with Haskell would come less than a year later, experience that would soon inspire my hobby research project to implement a Haskell-style type system within the Racket macro system. The majority of my career to date has followed more or less directly from that formative time.</p><p>After a decade, it seems fair to reflect a little on what I have learned, what I managed to achieve, and maybe more significantly, what I did <em>not</em>. Something one learns very quickly upon beginning any serious work on programming languages is that they are startlingly, unforgivably <em>hard</em>. A vast array of dizzyingly smart people have been working on the problem of program specification for seventy years; there are not many pieces of low hanging fruit left on the tree, and any new gains tend to be very hard won.</p><p>Still, even if programming language design is challenging, it would be some consolation if we were able to slowly yet steadily advance the state of the art, gradually picking away at the gap between the tools we have now and the ultimate programming language of the future. Sadly, even that pragmatic vision does not survive contact with reality. In practice, the challenge of programming language design is not one of expanding a well-defined frontier, it is grappling with a neverending list of fundamental <em>tradeoffs</em> between mutually incompatible features.</p><p>Subtyping is tantalizingly useful but makes complete type inference incredibly difficult (and in general, provably impossible). Structural typing drastically reduces the burden of assigning a <em>name</em> to every uninteresting intermediate form but more or less precludes taking advantage of Haskell-style typeclasses or Rust-style traits. Dynamic dispatch substantially assists decoupling of software components but can come with a significant performance cost without a JIT. Just-in-time compilation can use runtime information to optimize code in ways that permit more flexible coding patterns, but JIT performance can be unpredictable, and the overhead of an optimizing JIT is substantial. Sophisticated metaprogramming systems can radically cut down on boilerplate and improve program concision and even readability, but they can have substantial runtime or compile-time overhead and tend to thwart automated tooling. The list goes on and on.</p><p>The essential, most stubborn problems in programming languages come from unavoidable tensions between conflicting desires and requirements. We want loosely coupled software components that can be easily reused, but we also want the performance benefits of tight coupling and specialization. We want flexible programming languages that do not impose upon our freedom of expression, but we also want the benefits of static program analysis and powerful safety guarantees. We want sophisticated type systems that allow specifying ever more complex invariants, but we also want readable type signatures that won’t regularly end up longer than the code itself.</p><p>We cannot build the One True Programming Language because we cannot have everything at once, and we cannot hope to universally decide which tradeoffs are the right ones to make. This naturally makes the thought of a system constructed from a tapestry of many different programming languages attractive, affording the programmer the opportunity to select the tool best suited to the task at hand. Unfortunately, that, too, comes with (often brutal) tradeoffs of its own: the impedance mismatch at language boundaries, the vastly more complex tooling required for a polyglot system, and the increased knowledge burden of maintaining a system built in so many different ways.</p><p>Ten years of working on and thinking about programming languages has forced me to come to terms with a humbling truth: I do not know how to build a better programming language. I am personally extremely sympathetic to the idea that the future of programming probably involves more domain-specific languages, and I think the industry as a whole has been (very slowly) trending in that direction for quite some time. But even I acknowledge that the question of how precisely that ought to be done is extraordinarily complicated, and there are no easy answers here.</p><p>Even so, it would be far too dismal of me to suggest that the problem is hopeless, nor do I believe the work does not matter. Even if the problem has proven more difficult than teenage me may have hoped, the work <em>is</em> satisfying when it bears fruit. Unfortunately, even after toiling for years overcoming daunting problems, every working programming language enthusiast is forced to confront a much more frustrating enemy: <em>programmers</em>.</p><h3><a name="the-reactionary-conservatism-of-the-median-programmer"></a>The reactionary conservatism of the median programmer</h3><p>It should come as no surprise that programming language design is largely a social problem. Choice of programming language tends to be driven by strong network effects, and programmers tend to prefer languages that resemble what they already know. Programmers’ general lack of curiosity and outright hostility to learning new things has always been a personal frustration of mine, but I ultimately do understand much of where it comes from: as engineers, an overwhelming part of our job description is managing <em>risk</em>. Exotic technology choices are risky, and it is a responsible impulse to be wary of them.</p><p>Still, the history of mainstream programming languages is essentially a story of programmers vocally and emphatically rejecting what eventually proved to be some of the most incredibly successful innovations in the history of the field. Assembly programmers largely laughed at FORTRAN, but just a few decades later, there were nevertheless very few remaining assembly programmers. First-class functions were widely derided as needlessly complicated and confusing until programmers were forced to finally take the time to learn to use them once JavaScript became a load-bearing language by historical accident, and within a decade, they became a required feature for every major programming system. Sophisticated type systems largely retain a perception of overengineered, ivory-tower elitism, but many of the programmers who hold those very opinions have enthusiastically adopted Rust, a language that features a type system so complex that idiomatic Rust code can easily put Haskell programs to shame.</p><p>Discussions of programming languages are, by and large, emotionally driven shouting matches based on anecdotes and gut feelings. Surprisingly (or perhaps not), although most working programming language theorists and compiler engineers have a far more informed perspective on this subject (and do at least tend to refrain from petty debate), disparaging remarks directed towards languages not to the speaker’s taste are hardly unheard of even at academic conferences dedicated to the subject. I want to be very clear that I do not think that is somehow indicative of some toxicity within the community—they are by and large a group of truly amazing, wonderful people, and such comments are usually delivered with tongue planted firmly in cheek. All the same, my point is simply that programming languages are an emotional, opinionated subject in a way that seems perhaps inherent to the craft, <a href="/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/">a topic I explored at some length all the way back in 2019.</a></p><p>I do earnestly believe that the field of software engineering would benefit from more openness to new ideas and willingness to experiment with new technologies. I also think it would be enormously refreshing if programmers did not nearly universally speak with an expert’s confidence on subjects they have only passingly encountered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> Nevertheless, I do not expect programmers’ tribalism to diminish anytime soon, so the pragmatist in me understands their nature imposes real limitations on what we as a field can meaningfully accomplish.</p><p>In my experience, most working programming language researchers have essentially resigned themselves to this fact, and the prevailing wisdom is to almost entirely decouple the value of the work from its impact. Even if an idea is broadly ignored by programmers today, if it’s a good enough idea, it will still make its way into some programming language in some distant tomorrow, as many ideas eventually have. Lexical closures were first applied to programming languages in 1975 but took a full thirty years to be broadly accepted. Algebraic datatypes were first introduced around the same time and have only recently found mainstream acceptance through Rust. Innovations take a long, <em>long</em> time to make the jump from research to practice in this field, and placing one’s sense of professional accomplishment in the fickle hands of mainstream programmers’ whims and preferences is a recipe for personal misery.</p><p>I have nothing but respect for this attitude, genuinely. This research community is astonishingly friendly, warm, creative, fun, and accepting, especially given the vitriol these subjects tend to elicit from programmers at large. I feel genuinely honored to have had the opportunity to personally collaborate with many of them, and the collective output of endlessly fascinating ideas and exciting projects one can find each and every year at every major PL conference is consistently inspiring. All the same, as much respect as I have for the people who work tirelessly on these problems for their own sake, I am not sure I can continue to count myself among them. I do find the subject exciting, and I do consider it rewarding in its own way, but these past few years, I have slowly been coming to terms with the fact that I don’t think I find it quite rewarding enough.</p><h2><a name="why-i-write-computer-programs"></a>Why I write computer programs</h2><p>I like writing software. It is strange how radical a statement that sometimes feels. So many programmers seem to practically despise what they do for a living, at least as you’d hear them describe it. I have never really been able to empathize with that mindset. I find programming fun, and I derive a unique joy from writing code that decisively solves the problem at hand. It doesn’t have to be a particularly interesting problem, and the solution doesn’t even have to require much more than snapping blocks together, but as long as I can take some pride and satisfaction in the craftsmanship and end result, I am usually pretty happy.</p><p>My attraction to libraries, tooling, and compilers has always stemmed from a mixture of personal motivation and a certain satisfaction from improving things for my peers. One of my favorite things about software engineering is taking a problem and solving it in such a way that it largely <em>stays solved</em>. Sure, it may need to change as requirements evolve, but the solution continues to work even after the labor is done. Other tasks in my life do not provide nearly the same sense of accomplishment. When I do a load of laundry, it provides me with clean clothes for a week or two, but ultimately, I know it won’t be long before I’m going to have to do it again. Software engineering is not like that. Working on software—even extremely mundane software—provides a pleasant variety of tasks that can be well and truly <em>completed</em>. I like that sense of progress, and I appreciate the relaxing, almost meditative process of growing and maintaining the metaphorical garden that is a collection of code.</p><p>Given all of the above, it is not altogether clear why I would struggle to take satisfaction in my work on programming languages. If anything, working on a compiler seems like the ultimate realization of my aforementioned desires applied to the very thing I, myself, spend my day doing. If there is some annoying or repetitive task, and I can extend my programming system to provide a better way of doing it, that task is something I will never again need to do. What’s more, working on a compiler that thousands of people actually use means this is not just benefiting me, it’s benefiting everyone else as well. It feels good to have that kind of impact, and to know that my effort is essentially multiplied by everyone who happens to use it.</p><p>On paper, this ought to make working on a compiler like GHC enormously satisfying. Even if I feel frustrated by mainstream attitudes towards programming languages, Haskell programmers feel differently. Surely, working on something used and appreciated by Haskellers ought to feel very rewarding. But it wasn’t really. There were parts that were nice, yes, and the problems were often interesting. But I didn’t find it <em>rewarding</em> in the way I had hoped, and I’m not sure it even compared favorably to working on bog-standard software. Why?</p><h3><a name="on-the-haskell-community"></a>On the Haskell community</h3><p>This was the hardest section of this blog post to write. It is easy to write about the things I love and the people I admire. It is perhaps even easier to write about the things I despise and the people I disdain. The Haskell community is neither. I do not <em>dislike</em> it. Several of its members are people I consider very good friends. Nonetheless, I have never managed to fall in any particular love with it, and even if that is no sin on its part, it is a somewhat sad thing to have to confess.</p><p>I want to be <em>excruciatingly</em> clear that I do not wish to mount any indictment of the Haskell community or any of the people in it. Many people seem to have a preconception of Haskellers as mean and elitist, and while I do get the sense there have been some historically bad actors that contributed to that perception, my general impression is that those people have been thoroughly expunged. I have never been the target of any memorably rude conduct, and I’ve definitely never felt targeted by any prejudice or discrimination, and if anything I have always felt universally warmly welcomed at every meetup or conference I have attended. I cannot speak for anyone but myself, and my experiences are necessarily limited. I cannot hope to claim with any certainty that no abuse has ever occurred or that it does not continue to occur within any Haskell’s myriad social spaces, most of which I have never participated in. Still, I am happy to be able to say that I have never personally experienced any real unfriendliness, and that is certainly not a factor in my dispassion for the language’s community. If anything, I feel I must extend a very genuine <em>thank you</em> for all the lovely support I have received from Haskellers over the years, and it is that support and enthusiasm that so often kept me motivated for as long as I was.</p><p>This is all simply to say that I do not think it is the <em>fault</em> of anyone in particular for my lack of affinity for the Haskell community. Rather, I think perhaps it is just not a culture that I have ever especially related to. That probably does not even say very much, as I’m not sure there are many communities I <em>could</em> say that about. That says much more about me than it does about Haskell or Haskellers.</p><p>It is admittedly tempting to say that perhaps I find these communities alienating in part because they lack people I identify with and relate to. After all, it is simply true that the Haskell community is <em>overwhelmingly</em> male, which really has felt lonely at times. It’s also true that the Haskell community tends to attract a lot of people who are mainly interested in Haskell because they find it intellectually interesting, or because they like category theory and enjoy libraries like <code>lens</code> that have explored those ideas, while I am really only interested in Haskell insofar as it is a practical vehicle for writing useful software. Nevertheless, there <em>are</em> other women who write Haskell and there <em>are</em> other people who care very much about writing useful software in Haskell, and that doesn’t seem to have radically altered my relationship to the community, so I think it would be disingenuous of me to claim that either of those flaws are substantially to blame.</p><p>If there is one substantive frustration I can express with Haskell, it is where the money comes from. For as long as I have been involved with it, Haskell has predominantly been used and funded by a combination of fintech and cryptocurrency. I don’t really have anything against fintech, though I certainly don’t find it especially exciting, but I do have something against crypto, and those who followed me on Twitter before its untimely demise know I have always been pretty open about that fact. My thoughts on the matter are really not something I want to elaborate on here, but suffice to say it is not especially motivating to work on a language if all of its users are applying it in ways I feel essentially indifferent about at best and actively hostile to at worst. This is obviously not something I have any easy answers to, but it is probably the most significant, concrete factor in me losing my interest in working on GHC, so if you mourn me leaving, at least I can give you one semi-actionable reason.</p><p>Make no mistake: GHC is a truly incredible project that often still feels like a compiler from the future, and having the opportunity to work on it as a part of my job was about as technologically stimulating as I could possibly ask for. Even so, during my time at Tweag, I often found myself reflecting on the fact that the <em>users</em> I was working for were, ultimately, Haskell programmers, a class I’m not sure even really contains <em>me</em>. I have often reflected on the fact that, although I have many personal projects, I have <em>never</em> chosen Haskell for any of them. Not once. Whenever I need to write a little code to accomplish some task, it’s always Racket. This blog is in Racket. My personal shell utilities are in Racket. When I want to scrape a website or wrap some library, I do it with Racket. Haskell is a language I have always exclusively written professionally, and although I do very earnestly love many things about it, it is not so precious to me that I feel motivated to contribute to it out of passion alone. If there were more <em>users</em> of Haskell doing inspiring, exciting things with the tools I was working on, I might feel substantially more driven to indirectly contribute to those causes. But Haskell is a niche language used by a select few, and it is hard for me to spend so much of my life working on a technological marvel that practically nobody will ever benefit from.</p><p>As a final note on this subject: I do believe very earnestly that the functional programming and programming languages communities would benefit enormously from more demographic diversity. Even compared to software engineering as a whole, the gender ratio is, frankly, almost unbelievably grim. I have always been drawn to interests and hobbies that tend to skew male, and that has never really bothered me, so if <em>I</em> have found myself feeling alienated by the conspicuous absence of other women—to the point that, in the past, I have sometimes considered giving up on Haskell primarily for that reason—I don’t think it is outrageous to say things are pretty dire. A wider set of perspectives could, in theory, inject some refreshing creativity into the dreary ecosystem that is industrial Haskell. Of course, I will admit that I have absolutely no idea how that end could possibly be achieved, and it is a subject far broader and more complicated than I think belongs in this blog post. For now, I will leave things at that, but I hope my voice is sufficiently well regarded that some readers may take my comments to heart.</p><h2><a name="reflections-on-a-decade-s-work"></a>Reflections on a decade’s work</h2><p>Before writing this blog post, I essentially pitched it to a group of friends as “a sort of apology”. At times I certainly feel burdened by the weight of all the endeavors I’ve left unfinished, all the work I never carried long enough to bear fruit. Even so, as I write these words, I find myself reflecting on the things I feel I <em>did</em> manage to contribute these past ten years, and it strikes me that the trail I’ve left behind is perhaps not so dismal, after all.</p><p>It is comforting to look back on a decade’s accumulations and realize there is enough to take some real pride in. I am proud of my contributions to Racket and Haskell, and of my numerous libraries in each of them. I am proud of my experiments, among them <a href="https://github.com/lexi-lambda/hackett">Hackett</a> and <a href="https://github.com/lexi-lambda/eff">eff</a>, and the excellent work from others they have inspired. I am proud of this very blog, from the <a href="/blog/2019/11/05/parse-don-t-validate/">most impactful</a> things I’ve written to the <a href="/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/">far more niche</a>. I am proud of my <a href="https://langdev.stackexchange.com/users/861/alexis-king?tab=answers&amp;sort=newest">contributions to Programming Languages Stack Exchange</a>, a site I most certainly intend to remain a moderator of and will undoubtedly continue to contribute to. And I am proud of the <a href="https://www.youtube.com/watch?v=0jI-AlWEwYI">numerous talks</a> I have <a href="https://www.youtube.com/watch?v=TE48LsgVlIU">presented over the years</a>, all of them labors of love.</p><p>I have not always accomplished all the things I set out to achieve. Some have, for some reason or another, gotten the better of me. Many of them, from my <a href="https://github.com/lexi-lambda/blackboard">math typesetting system</a> to my <a href="https://github.com/ghc-proposals/ghc-proposals/pull/303">improvements to GHC’s arrow notation</a>, are things I hope to one day push over the finish line. But for now it is clear my heart is no longer really in them, so I think it is time for me to let them go. Perhaps others will find inspiration in them. Perhaps you will succeed where I did not.</p><p>It is a little sad to think about moving on from such a substantial chapter of my life. To date, I have spent almost my entire career in this professional niche, and I suspect I will never fully leave it. It has been a pleasure to work with so many bright, lovely, inspiring people, and perhaps one day I will find myself eager to return once more. But for now, my interests are elsewhere, and despite the bittersweet feelings, there is also a very real excitement in the tantalizing opportunity to do something new.</p><h2><a name="what-next"></a>What next?</h2><p>It is a little amusing to think that what I find myself most drawn to after a decade of pursuing ever more exotic projects is something entirely mundane: I want to write utterly ordinary software. I want to work on a piece of software used by people to accomplish a task, and I want to take pride and satisfaction in doing it well.</p><p>I do not yet really have any idea what that might look like. It has been over six years since I last worked on anything that really feels like it could possibly fit that description, and even then, it was only one part of my job. Is it so strange that after spending fifteen years of my life trying everything I could to get away from spending all day wrangling a big ball of mud in a Java IDE, that experience sounds pretty cozy to return to?</p><p>In late 2022, I decided on a whim to reverse engineer and resurrect a 2008 Java browser game called <a href="https://github.com/lexi-lambda/shattered-plans">Shattered Plans</a>. While doing so, I had the opportunity to spend an awful lot of time in IntelliJ, and after years of acclimatizing to writing code in fanciful languages with nothing more than a plain text editor, a terminal emulator, and a dream, the experience was almost viscerally thrilling. They say the grass is always greener, but even if that is true, I think I’d like to find it out for myself.</p><p>More to the point, my life is just very different now from how it was for most of my twenties. I was single, and it was easy to justify spending almost all of my time thinking about code, whether on the clock or not. It was hardly unheard of for me to spend twelve or even sixteen hours at my desk, just tinkering on something that had captured my attention. It was fun, and I’m glad I did it, but I am also very ready to work a simple, regular job I don’t feel the need to permanently devote a large portion of my soul to. It isn’t very romantic, but then, I have other things for that.</p><p>If you think you have a position that meets those (admittedly extremely broad) criteria, and you think you might like to hire me, by all means: <a href="mailto:lexi.lambda@gmail.com">send me an email</a>. The worst I can do is not reply. And who knows? With the burden of my own expectations behind me, perhaps I’ll even blow the dust off this old blog of mine and start writing things again. As with my job, whatever I might choose to write next I cannot say. Either way, it’s a little exciting to be able to treat myself to a blank slate.</p><p>I could probably spend twenty thousand words writing in circles about all my thoughts and feelings on this subject. However, to be honest, I don’t especially want to. As this blog post has hopefully communicated, I would like to be done, and so this will have to be enough. Allow me to personally thank all of you who have indulged me by reading my disorganized ramblings to the end.</p><p>It’s been a long time. Here’s to another ten years.</p><ol class="footnotes"><li id="footnote-1"><p>Readers may find it clarifying to note that a great deal of my early programming experience was writing (then era-appropriate) Java 6. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I have often wondered if other engineering disciplines have anything to rival the collective overconfidence of the average Hacker News commenter. This is an earnest curiosity, not merely a rhetorical flourish; I do sometimes wonder what it is about my field of choice that engenders such disregard for the value of expertise. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>An introduction to typeclass metaprogramminghttps://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/https://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/25 Mar 2021<article><p><em>Typeclass metaprogramming</em> is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem), and it is the core mechanism used to implement generic programming via <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">GHC generics</a>. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.</p><p>This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does <em>not</em> attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also <em>not</em> a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.</p><h2><a name="part-1-basic-building-blocks"></a>Part 1: Basic building blocks</h2><p>Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.</p><p>To start, we’ll cover the absolute foundations of typeclass metaprogramming.</p><h3><a name="typeclasses-as-functions-from-types-to-terms"></a>Typeclasses as functions from types to terms</h3><p>As its name implies, typeclass metaprogramming (henceforth TMP<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup>) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic <code>==</code> operator via the <code>Eq</code> class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: <strong>typeclasses are functions from types to (runtime) terms</strong>.</p><p>What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called <code>TypeOf</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Given these instances, we can observe that they do what we expect in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>Note that both the <code>TypeOf Bool</code> and <code>TypeOf Char</code> instances ignore the argument to <code>typeOf</code> altogether. This makes sense, as the whole point of the <code>TypeOf</code> class is to get access to <em>type</em> information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This typeclass definition is a little unusual, as the type parameter <code>a</code> doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>the full type of the <code>show</code> method is implicitly extended with a <code>Show a</code> constraint to yield:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Furthermore, if we write <code>forall</code>s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the <em>full</em> type of <code>show</code>:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>In the same vein, we can write out the full type of <code>typeOf</code>, as given by our new definition of <code>TypeOf</code>:</p><pre><code class="pygments"><span class="nf">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This type is still unusual, as the <code>a</code> type parameter doesn’t appear anywhere to the right of the <code>=&gt;</code> arrow. This makes the type parameter trivially <em>ambiguous</em>, which is to say it’s impossible for GHC to infer what <code>a</code> should be at any call site. Fortunately, <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html">we can use <code>TypeApplications</code></a> to pass a type for <code>a</code> directly, as we can see in the updated definition of <code>TypeOf (a, b)</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Once again, we can test out our new definitions in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="kt">Bool</span> +<span class="s">"Bool"</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Bool</span><span class="p">,</span><span class="w"> </span><span class="kt">Char</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our <code>typeOf</code> function is, quite literally, a function that accepts a single type as an argument and returns a term-level <code>String</code>. Of course, the <code>TypeOf</code> typeclass is not a particularly <em>useful</em> example of such a function, but it demonstrates how easy it is to construct.</p><h3><a name="type-level-interpreters"></a>Type-level interpreters</h3><p>One important consequence of eliminating the value-level argument of <code>typeOf</code> is that there is no need for its argument type to actually be <em>inhabited</em>. For example, consider the <code>TypeOf</code> instance on <code>Void</code> from <code>Data.Void</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Void</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Void"</span></code></pre><p>This above instance is no different from the ones on <code>Bool</code> and <code>Char</code> even though <code>Void</code> is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of <code>TypeOf</code> on lists:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"["</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"]"</span></code></pre><p>If <code>typeOf</code> required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type <code>a</code> to recursively apply <code>typeOf</code> to. But since <code>typeOf</code> only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.</p><p>A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Z</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="n">a</span></code></pre><p>It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:</p><ul><li><p><code>Z</code> is a type that represents 0.</p></li><li><p><code>S Z</code> is a type that represents 1.</p></li><li><p><code>S (S Z)</code> is a type that represents 2.</p></li></ul><p>And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Numeric.Natural</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>As its name implies, <code>reifyNat</code> reifies a type-level natural number encoded using our datatypes above into a term-level <code>Natural</code> value:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="kt">Z</span> +<span class="mi">0</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span> +<span class="mi">1</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="mi">2</span></code></pre><p>One way to think about <code>reifyNat</code> is as an <em>interpreter</em> of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.</p><h3><a name="overlapping-instances"></a>Overlapping instances</h3><p>Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for <code>Show (Maybe a)</code>, you aren’t supposed to <em>also</em> write an instance for <code>Show (Maybe Bool)</code>, since it isn’t clear whether <code>show (Just True)</code> should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.</p><p>Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.</p><p>As a simple example, suppose we wanted to write a typeclass that checks whether a given type is <code>()</code> or not:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:</p><pre><code class="pygments"><span class="c1">-- not actually valid Haskell, just an example</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>But if we try to translate this to typeclass instances, we’ll get a problem:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> This means GHC will complain about instance overlap if we try to evaluate <code>isUnit @()</code>:</p><pre><code>ghci&gt; isUnit @() + +error: + • Overlapping instances for IsUnit () + arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance IsUnit () +</code></pre><p>To fix this, we have to explicitly mark <code>IsUnit ()</code> as overlapping:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>Now GHC accepts the expression without complaint:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="nb">()</span> +<span class="kt">True</span></code></pre><p>What does the <code>{-# OVERLAPPING #-}</code> pragma do, exactly? The gory details are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances">spelled out in the GHC User’s Guide</a>, but the simple explanation is that <code>{-# OVERLAPPING #-}</code> relaxes the overlap checker as long as the instance is <em>strictly more specific</em> than the instance(s) it overlaps with. In this case, that is true: <code>IsUnit ()</code> is trivially more specific than <code>IsUnit a</code>, since the former only matches <code>()</code> while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.</p><p>Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>The intent of <code>guardUnit</code> is to use <code>isUnit</code> to detect if its argument is of type <code>()</code>, and if it is, to return an error. However, even though we marked <code>IsUnit ()</code> overlapping, we still get an overlapping instance error:</p><pre><code>error: + • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance [overlapping] IsUnit () + • In the expression: isUnit @a +</code></pre><p>What gives? The problem is that GHC simply doesn’t know what type <code>a</code> is when compiling <code>guardUnit</code>. It <em>could</em> be instantiated to <code>()</code> where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.</p><p>This behavior is actually a very, very good thing. If GHC were to blindly pick the <code>IsUnit a</code> instance in this case, then <code>guardUnit</code> would always take the <code>False</code> branch, even when passed a value of type <code>()</code>! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when <code>{-# OVERLAPPING #-}</code> annotations are used, so it’s important to keep their limitations in mind.</p><p>As it happens, in this particular case, the error is easily remedied. We simply have to add an <code>IsUnit</code> constraint to the type signature of <code>guardUnit</code>:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now picking the right <code>IsUnit</code> instance is deferred to the place where <code>guardUnit</code> is used, and the definition is accepted.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><h3><a name="type-families-are-functions-from-types-to-types"></a>Type families are functions from types to types</h3><p>In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE TypeFamilies #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>The above is a <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families">closed type family</a>, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of <code>Sum</code> would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="nf">sum</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span> +<span class="nf">sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="nf">sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="n">sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.</p><p>To test our definition of <code>Sum</code> in GHCi, we can use <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind">the <code>:kind!</code> command</a>, which prints out a type and its kind after reducing it as much as possible:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span></code></pre><p>We can also combine <code>Sum</code> with our <code>ReifyNat</code> class from earlier:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.</p><h3><a name="example-1-generalized-concat"></a>Example 1: Generalized <code>concat</code></h3><p>Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a <code>flatten</code> function similar to like-named functions provided by many dynamically-typed languages. In those languages, <code>flatten</code> is like <code>concat</code>, but it works on a list of arbitrary depth. For example, we might use it like this:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]</span></code></pre><p>In Haskell, lists of different depths have different types, so multiple levels of <code>concat</code> have to be applied explicitly. But using TMP, we can write a generic <code>flatten</code> function that operates on lists of any depth!</p><p>Since this is <em>typeclass</em> metaprogramming, we’ll unsurprisingly begin with a typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="o">???</span><span class="p">]</span></code></pre><p>Our first challenge is writing the return type of <code>flatten</code>. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>Now we can write our <code>Flatten</code> instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>The inductive case is when the type is a nested list, in which case we want to apply <code>concat</code> and recur:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">(</span><span class="n">concat</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Sadly, if we try to compile these definitions, GHC will reject our <code>Flatten [a]</code> instance:</p><pre><code>error: + • Couldn't match type ‘a’ with ‘ElementOf [a]’ + ‘a’ is a rigid type variable bound by + the instance declaration + Expected type: [ElementOf [a]] + Actual type: [a] + • In the expression: x + In an equation for ‘flatten’: flatten x = x + In the instance declaration for ‘Flatten [a]’ + | + | flatten x = x + | ^ +</code></pre><p>At first blush, this error looks very confusing. Why doesn’t GHC think <code>a</code> and <code>ElementOf [a]</code> are the same type? Well, consider what would happen if we picked a type like <code>[Int]</code> for <code>a</code>. Then <code>[a]</code> would be <code>[[Int]]</code>, a nested list, so the first case of <code>ElementOf</code> would apply. Therefore, GHC refuses to pick the second equation of <code>ElementOf</code> so hastily.</p><p>In this particular case, we might think that’s rather silly. After all, if <code>a</code> were <code>[Int]</code>, then GHC wouldn’t have picked the <code>Flatten [a]</code> instance to begin with, it would pick the more specific <code>Flatten [[a]]</code> instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.</p><p>Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our <code>Flatten [a]</code> instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>This is a <em>type equality constraint</em>. Type equality constraints are written with the syntax <code>a ~ b</code>, and they state that <code>a</code> must be the same type as <code>b</code>. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that <code>ElementOf [a]</code> must always be <code>a</code>, which allows the instance to typecheck.</p><p>Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must <em>eventually</em> be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the <code>Flatten [a]</code> instance is selected, GHC will know that <code>a</code> is <em>not</em> a list type, and it will be able to reduce <code>ElementOf [a]</code> to <code>a</code> without difficulty. Indeed, we can see this for ourselves by using <code>flatten</code> in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span></code></pre><p>It works! But why do we need the type annotation on <code>1</code>? If we leave it out, we get a rather hairy type error:</p><pre><code>error: + • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ + Expected type: [ElementOf [a]] + Actual type: [ElementOf [a0]] + NB: ‘ElementOf’ is a non-injective type family + The type variable ‘a0’ is ambiguous +</code></pre><p>The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a <code>Num [a]</code> instance, in which case <code>1</code> could actually have a list type, and either case of <code>ElementOf</code> could match depending on the choice of <code>Num</code> instance. Of course, no such <code>Num</code> instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.</p><p>This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call <code>flatten</code> on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.</p><p>That wrinkle aside, <code>flatten</code> is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.</p><h4><a name="typeclasses-as-compile-time-code-generation"></a>Typeclasses as compile-time code generation</h4><p>Presented with the above definition of <code>Flatten</code>, it might not be immediately obvious how to think about <code>Flatten</code> as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, <code>Eq</code> or <code>Show</code>) than the <code>TypeOf</code> and <code>ReifyNat</code> classes we defined above.</p><p>One useful way to shift our perspective is to consider equivalent <code>Flatten</code> instances written using point-free style:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">id</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">concat</span></code></pre><p>These definitions of <code>flatten</code> no longer (syntactically) depend on term-level arguments, just like our definitions of <code>typeOf</code> and <code>reifyNat</code> didn’t accept any term-level arguments above. This allows us to consider what <code>flatten</code> might “expand to” given a type argument alone:</p><ul><li><p><code>flatten @[Int]</code> is just <code>id</code>, since the <code>Flatten [a]</code> instance is selected.</p></li><li><p><code>flatten @[[Int]]</code> is <code>flatten @[Int] . concat</code>, since the <code>Flatten [[a]]</code> instance is selected. That then becomes <code>id . concat</code>, which can be further simplified to just <code>concat</code>.</p></li><li><p><code>flatten @[[[Int]]]</code> is <code>flatten @[[Int]] . concat</code>, which simplifies to <code>concat . concat</code> by the same reasoning above.</p></li><li><p><code>flatten @[[[[Int]]]]</code> is then <code>concat . concat . concat</code>, and so on.</p></li></ul><p>This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of <code>flatten</code> takes a type as an argument and produces some number of composed <code>concat</code>s as a result. From this perspective, <code>Flatten</code> is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.</p><p>This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name <em>metaprogramming</em>. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.</p><h2><a name="part-2-generic-programming"></a>Part 2: Generic programming</h2><p>Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.</p><p>In the previous section, we discussed how to use TMP to write a generic <code>flatten</code> operation. In this section, we’ll aim a bit higher: totally generic functions that operate on <em>arbitrary</em> datatypes.</p><h3><a name="open-type-families-and-associated-types"></a>Open type families and associated types</h3><p>Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, <em>open type families</em>. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using <code>type instance</code> declarations. For example, we could define our <code>Sum</code> family from above like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>In the case of <code>Sum</code>, this would not be very useful, and indeed, <code>Sum</code> is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.</p><p>This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a <code>Key</code> open type family that relates types to the types used to index them:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span></code></pre><p>This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>In this case, anyone could define their own data structure, define instances of <code>Key</code> and <code>HasKey</code> for their data structure, and use <code>hasKey</code> to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>An open family declared inside a typeclass like this is called an <em>associated type</em>. It works exactly the same way as the separate definitions of <code>Key</code> and <code>HasKey</code>, it just uses a different syntax. Note that although the <code>family</code> and <code>instance</code> keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).</p><p>Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like <a href="https://hackage.haskell.org/package/mono-traversable"><code>mono-traversable</code></a> are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.</p><p>However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes <em>heavy</em> use of open type families: datatype-generic programming.</p><h3><a name="example-2-datatype-generic-programming"></a>Example 2: Datatype-generic programming</h3><p><em>Datatype-generic programming</em> refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include</p><ul><li><p>equality, comparison, and hashing,</p></li><li><p>recursive traversal of self-similar data structures, and</p></li><li><p>serialization and deserialization,</p></li></ul><p>among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on <em>any</em> datatype.</p><p>In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.</p><h4><a name="generic-datatype-representations"></a>Generic datatype representations</h4><p>At the heart of the <code>Generic</code> class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Authentication</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AuthBasic</span><span class="w"> </span><span class="kt">Username</span><span class="w"> </span><span class="kt">Password</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AuthSSH</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>then we have a type that is essentially equivalent to this one:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>If we know how to define a function on a nested tree built out of <code>Either</code>s and pairs, then we know how to define it on <em>any</em> such datatype! This is where TMP comes in: recall the way we viewed <code>Flatten</code> as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?</p><p>The answer to that question is <em>yes</em>. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, <code>numFields (AuthBasic "alyssa" "pass1234")</code> would return <code>2</code>, while <code>numFields (AuthSSH "&lt;key&gt;")</code> would return <code>1</code>. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.</p><p>We’ll start by using TMP to implement a “generic” version of <code>numFields</code> that operates on trees of <code>Either</code>s and pairs as described above:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="c1">-- base case: leaf value</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>Just like our <code>Flatten</code> class from earlier, <code>GNumFields</code> uses the type-level structure of its argument to choose what to do:</p><ul><li><p>If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.</p></li><li><p>If we find <code>Left</code> or <code>Right</code>, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.</p></li><li><p>In the case of any other value, we’re at a “leaf” in the tree of <code>Either</code>s and pairs, which corresponds to a single field, so we just return <code>1</code>.</p></li></ul><p>Now if we call <code>gnumFields (Left ("alyssa", "pass1234"))</code>, we’ll get <code>2</code>, and if we call <code>gnumFields (Right "&lt;key&gt;")</code>, we’ll get <code>1</code>. All that’s left to do is write a bit of code that converts our <code>Authentication</code> type to a tree of <code>Either</code>s and pairs:</p><pre><code class="pygments"><span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericizeAuthentication</span></code></pre><p>Now we get the results we want on our <code>Authentication</code> type using <code>numFieldsAuthentication</code>, but we’re not done yet, since it only works on <code>Authentication</code> values. Is there a way to define a generic <code>numFields</code> function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFields</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericize</span></code></pre><p>Now <code>numFields (AuthBasic "alyssa" "pass1234")</code> returns <code>2</code>, as desired, and it will <em>also</em> work with any datatype that provides a <code>Generic</code> instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:</p><ul><li><p>First, we define the <code>Generic</code> class, comprised of two parts:</p><ol><li><p>The <code>Rep a</code> associated type maps a type <code>a</code> onto its generic, sums-of-products representation, i.e. one built out of combinations of <code>Either</code> and pairs.</p></li><li><p>The <code>genericize</code> method converts an actual <em>value</em> of type <code>a</code> to the equivalent value using the sums-of-products representation.</p></li></ol></li><li><p>Next, we define a <code>Generic</code> instance for <code>Authentication</code>. <code>Rep Authentication</code> is the sums-of-products representation we described above, and <code>genericize</code> is likewise <code>genericizeAuthentication</code> from above.</p></li><li><p>Finally, we define <code>numFields</code> as a function with a <code>GNumFields (Rep a)</code> constraint. This is where all the magic happens:</p><ul><li><p>When we apply <code>numFields</code> to a datatype, <code>Rep</code> retrieves its generic, sums-of-products representation type.</p></li><li><p>The <code>GNumFields</code> class then uses various TMP techniques we’ve already described so far in this blog post to generate a <code>numFields</code> implementation on the fly from the structure of <code>Rep a</code>.</p></li><li><p>Finally, that generated <code>numFields</code> implementation is applied to the genericized term-level value, and the result is produced.</p></li></ul></li></ul><p>After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) <code>numFields</code> operation. Surely just defining <code>numFields</code> on each type directly would be far easier? Indeed, if we were just considering <code>numFields</code>, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined <code>numFields</code>, and all of them would automatically work on <code>Authentication</code> because they all leverage the same <code>Generic</code> instance!</p><p>This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation <em>once</em>, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.</p><h4><a name="improving-our-definition-of-generic"></a>Improving our definition of <code>Generic</code></h4><p>You may note that the definition of <code>Generic</code> provided above does not match the one in <code>GHC.Generic</code>. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a <code>GHC.Generics</code> tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.</p><h5><a name="distinguishing-leaves-from-the-spine"></a>Distinguishing leaves from the spine</h5><p>One problem with our version of <code>Generic</code> is that it provides no way to distinguish an <code>Either</code> or pair that should be considered a “leaf”, as in a type like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">A</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">B</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">)</span></code></pre><p>Given this type, <code>Rep Foo</code> should be <code>Either (Either Int String) (Char, Bool)</code>, and <code>numFields (Right ('a', True))</code> will erroneously return <code>2</code> rather than <code>1</code>. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">getLeaf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Now our <code>Generic</code> instances look like this:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">PublicKey</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">key</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">))</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">A</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">B</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Since the <code>Leaf</code> constructor now distinguishes a leaf, rather than the absence of an <code>Either</code> or <code>(,)</code> constructor, we’ll have to update our <code>GNumFields</code> instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.</p><h5><a name="handling-empty-constructors"></a>Handling empty constructors</h5><p>Suppose we have a type with nullary data constructors, like the standard <code>Bool</code> type:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>How do we write a <code>Generic</code> instance for <code>Bool</code>? Using just <code>Either</code>, <code>(,)</code>, and <code>Leaf</code>, we can’t, but if we are willing to add a case for <code>()</code>, we can use it to denote nullary constructors:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="nb">()</span></code></pre><p>In a similar vein, we could use <code>Void</code> to represent datatypes that don’t have any constructors at all.</p><h4><a name="continuing-from-here"></a>Continuing from here</h4><p>The full version of <code>Generic</code> has a variety of further improvements useful for generic programming, including:</p><ul><li><p>Support for converting from <code>Rep a</code> to <code>a</code>.</p></li><li><p>Special indication of self-recursive datatypes, making generic tree traversals possible.</p></li><li><p>Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.</p></li><li><p>Fully automatic generation of <code>Generic</code> instances via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric">the <code>DeriveGeneric</code> extension</a>, which reduces the per-type boilerplate to essentially nothing.</p></li></ul><p>The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.</p><h2><a name="part-3-dependent-typing"></a>Part 3: Dependent typing</h2><p>It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.</p><h3><a name="datatype-promotion"></a>Datatype promotion</h3><p>In part 1, we used uninhabited datatypes like <code>Z</code> and <code>S a</code> to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, <code>True</code> and <code>False</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">True</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">False</span></code></pre><p>Now we could define type families to provide operations on these types, such as <code>Not</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>However, this has some frustrating downsides:</p><ul><li><p>First, it’s simply inconvenient that we have to define these new <code>True</code> and <code>False</code> “dummy” types, which are completely distinct from the <code>Bool</code> type provided by the prelude.</p></li><li><p>More significantly, it means <code>Not</code> has a very unhelpful kind:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span></code></pre><p>Even though <code>Not</code> is only <em>supposed</em> to be applied to <code>True</code> or <code>False</code>, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like <code>Not Char</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span> +<span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span></code></pre><p>Rather than getting an error, GHC simply spits <code>Not Char</code> back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.</p></li></ul><p>One way to think about <code>Not</code> is that it is largely <em>dynamically kinded</em> in the same way some languages are dynamically typed. That isn’t entirely true, as we technically <em>will</em> get a kind error if we try to apply <code>Not</code> to a type constructor rather than a type, such as <code>Maybe</code>:</p><pre><code>ghci&gt; :kind! Not Maybe + +&lt;interactive&gt;:1:5: error: + • Expecting one more argument to ‘Maybe’ + Expected a type, but ‘Maybe’ has kind ‘* -&gt; *’ +</code></pre><p>…but <code>*</code> is still a very big kind, much bigger than we would like to permit for <code>Not</code>.</p><p>To help with both these problems, GHC provides <em>datatype promotion</em> via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html">the <code>DataKinds</code> language extension</a>. The idea is that for each normal, non-GADT type definition like</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>then in addition to the normal type constructor and value constructors, GHC also defines several <em>promoted</em> constructors:</p><ul><li><p><code>Bool</code> is allowed as both a type and a kind.</p></li><li><p><code>'True</code> and <code>'False</code> are defined as new types of kind <code>Bool</code>.</p></li></ul><p>We can see this in action if we remove our <code>data True</code> and <code>data False</code> declarations and adjust our definition of <code>Not</code> to use promoted constructors:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DataKinds #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;True</span></code></pre><p>Now the inferred kind of <code>Not</code> is no longer <code>* -&gt; *</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>Consequently, we will now get a kind error if we attempt to apply <code>Not</code> to anything other than <code>'True</code> or <code>'False</code>:</p><pre><code>ghci&gt; :kind! Not Char + +&lt;interactive&gt;:1:5: error: + • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ +</code></pre><p>This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">&#39;Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">&#39;S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>Note that we need to add an explicit kind signature on the definition of the <code>ReifyNat</code> typeclass, since otherwise GHC will assume <code>a</code> has kind <code>*</code>, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that <code>Z</code> and <code>S</code> are related, this prevents someone from coming along and defining a nonsensical instance like <code>ReifyNat Char</code>, which previously would have been allowed but will now be rejected with a kind error.</p><p>Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.</p><h3><a name="gadts-and-proof-terms"></a>GADTs and proof terms</h3><p>So far in this blog post, we have discussed several different function-like things:</p><ul><li><p>Ordinary Haskell functions are functions from terms to terms.</p></li><li><p>Type families are functions from types to types.</p></li><li><p>Typeclasses are functions from types to terms.</p></li></ul><p>A curious reader may wonder about the existence of a fourth class of function:</p><ul><li><p><em>???</em> are functions from terms to types.</p></li></ul><p>To reason about what could go in the <em>???</em> above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?</p><p>In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.</p><p>GADTs<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup> are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html">described in detail in the GHC User’s Guide</a>, but the key idea for our purposes is that <em>pattern-matching on a GADT constructor can refine type information</em>. Here’s a simple, silly example:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Bool</span> +<span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Int</span> + +<span class="nf">doSomething</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">x</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span></code></pre><p>Here, <code>WhatIsIt</code> is a datatype with two nullary constructors, <code>ABool</code> and <code>AnInt</code>, similar to a normal, non-GADT datatype like this one:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AnInt</span></code></pre><p>What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, <code>ABool</code> and <code>AnInt</code> would both have the type <code>forall a. WhatIsIt a</code>, but in the GADT definition, we explicitly fix <code>a</code> to <code>Bool</code> in the type of <code>ABool</code> and to <code>Int</code> in the type of <code>AnInt</code>.</p><p>This simple feature allows us to do very interesting things. The <code>doSomething</code> function is polymorphic in <code>a</code>, but on the right-hand side of the first equation, <code>x</code> has type <code>Bool</code>, while on the right-hand side of the second equation, <code>x</code> has type <code>Int</code>. This is because the <code>WhatIsIt a</code> argument effectively constrains the type of <code>a</code>, as we can see by experimenting with <code>doSomething</code> in GHCi:</p><pre><code>ghci&gt; doSomething ABool True +False +ghci&gt; doSomething AnInt 10 +11 +ghci&gt; doSomething AnInt True + +error: + • Couldn't match expected type ‘Int’ with actual type ‘Bool’ + • In the second argument of ‘doSomething’, namely ‘True’ + In the expression: doSomething AnInt True + In an equation for ‘it’: it = doSomething AnInt True +</code></pre><p>One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The <code>ABool</code> constructor is a proof of <code>a ~ Bool</code>, while the <code>AnInt</code> constructor is a proof of <code>a ~ Int</code>. When you construct <code>ABool</code> or <code>AnInt</code>, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.</p><p>GADTs can be much more sophisticated than our simple <code>WhatIsIt</code> type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:</p><pre><code class="pygments"><span class="kr">infixr</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span> + +<span class="kr">data</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">HCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This type is a <em>heterogenous list</em>, a list that can contain elements of different types:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">t</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Num</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[Bool, [Char]</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>An <code>HList</code> is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total <code>head</code> function on <code>HList</code>s like this:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Remarkably, GHC does not complain that this definition of <code>head</code> is non-exhaustive. Since we specified that the argument must be of type <code>HList (a ': as)</code> in the type signature for <code>head</code>, GHC knows that the argument <em>cannot</em> be <code>HNil</code> (which would have the type <code>HList '[]</code>), so it doesn’t ask us to handle that case.</p><p>These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.</p><h4><a name="proofs-that-work-together"></a>Proofs that work together</h4><p>This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an <code>HList</code> of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with <code>head</code>, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.</p><p>However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a <em>proof term</em> that contains no values, it just encapsulates type equalities on a type-level list:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a]</span> +<span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b]</span> +<span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b, c]</span></code></pre><p>We call it a proof term because a value of type <code>OneToThree a b c as</code> constitutes a <em>proof</em> that <code>as</code> has exactly 1, 2, or 3 elements. Using <code>OneToThree</code>, we can write a function that accepts an <code>HList</code> accompanied by a proof term:</p><pre><code class="pygments"><span class="nf">sumUpToThree</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">z</span></code></pre><p>As with <code>head</code>, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:</p><ul><li><p>When we match on the <code>OneToThree</code> proof term, information flows from the term level to the type level, refining the type of <code>as</code> in that branch.</p></li><li><p>The refined type of <code>as</code> then flows back down to the term level, restricting the shape the <code>HList</code> can take and refinine the set of patterns we have to match.</p></li></ul><p>Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an <code>HList</code> has an even number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This is a proof which itself has inductive structure: <code>EvenCons</code> takes a proof that <code>as</code> has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an <code>HList</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span> + +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Once again, this definition is completely exhaustive, and we can show that it works in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="kt">EvenNil</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.</p><h4><a name="proof-inference"></a>Proof inference</h4><p>While our definition of <code>pairUp</code> is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the <code>Even</code> proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.</p><p>Remember that typeclasses are functions from types to terms. As its happens, a value of type <code>Even as</code> can be mechanically produced from the structure of the type <code>as</code>. This suggests that we could use TMP to automatically generate <code>Even</code> proofs, and indeed, we can. In fact, it’s not at all complicated:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>We can now adjust our <code>pairUp</code> function to use <code>IsEven</code> instead of an explicit <code>Even</code> argument:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>This is essentially identical to its old definition, but by acquiring the proof via <code>IsEven</code> rather than passing it explicitly, we can call <code>pairUp</code> without having to construct a proof manually:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This is rather remarkable. Using TMP, we are able to get GHC to <em>automatically construct a proof that a list is even</em>, with no programmer guidance beyond writing the <code>IsEven</code> typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: <code>IsEven</code> is a function that accepts a type-level list and generates an <code>Even</code> proof term.</p><p>From this perspective, <strong>typeclasses are a way of specifying a proof search algorithm</strong> to the compiler. In the case of <code>IsEven</code>, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.</p><h3><a name="aside-gadts-versus-type-families"></a>Aside: GADTs versus type families</h3><p>Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.</p><p>Consider again the type of the <code>pairUp</code> function above (without the typeclass for simplicity):</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>We used both a GADT, <code>Even</code>, and a type family, <code>PairUp</code>. But we could have, in theory, used <em>only</em> a GADT and eliminated the type family altogether. Consider this variation on the <code>Even</code> proof term:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="p">((</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span></code></pre><p>This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of <code>as</code> as an “input” parameter and <code>bs</code> as an “output” parameter. The idea is that any <code>EvenPairs</code> proof relates both an even-length list type and its paired up equivalent:</p><ul><li><p><code>EvenNil</code> has type <code>EvenPairs '[] '[]</code>,</p></li><li><p><code>EvenCons EvenNil</code> has type <code>EvenPairs '[a, b] '[(a, b)]</code>,</p></li><li><p><code>EvenCons (EvenCons EvenNil)</code> has type <code>EvenPairs '[a, b, c, d] '[(a, b), (c, d)]</code>,</p></li><li><p>…and so on.</p></li></ul><p>This allows us to reformulate our <code>pairUp</code> type signature this way:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">bs</span></code></pre><p>The definition is otherwise unchanged. The <code>PairUp</code> type family is completely gone, because now <code>EvenPairs</code> itself defines the relation. In this way, GADTs can be used like type-level functions!</p><p>The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Kind</span><span class="w"> </span><span class="p">(</span><span class="kt">Constraint</span><span class="p">)</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span></code></pre><p>The idea here is that <code>IsEvenTF as</code> produces a constraint can only be satisfied if <code>as</code> has an even number of elements, since that’s the only way it will eventually reduce to <code>()</code>, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting <code>IsEvenTF as =&gt;</code> in a type signature successfully restricts <code>as</code> to be an even-length list, but it doesn’t allow us to write <code>pairUp</code>. To see why, we can try the following definition:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Unlike the version using the GADT, this version of <code>pairUp</code> is not considered exhaustive:</p><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil +</code></pre><p>This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by <code>IsEvenTF</code> provides no term-level evidence about the shape of <code>as</code>, so we can’t branch on it the way we can branch on the <code>Even</code> GADT.<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> (In a sense, <code>IsEvenTF</code> is doing <a href="/blog/2019/11/05/parse-don-t-validate/">validation, not parsing</a>.)</p><p>For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.</p><h3><a name="guiding-type-inference"></a>Guiding type inference</h3><p>So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.</p><p>However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.</p><p>To illustrate what that can look like, suppose we want to use TMP to generate an <code>HList</code> full of <code>()</code> values of an arbitrary length:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Testing in GHCi, we can see it behaves as desired:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[(), (), ()]</span> +<span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>Now suppose we write a function that accepts a list containing exactly one element and returns it:</p><pre><code class="pygments"><span class="nf">unsingleton</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[a]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unsingleton</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Naturally, we would expect these to compose without a hitch. If we write <code>unsingleton unitList</code>, our TMP should generate a list of length 1, and we should get back <code>()</code>. However, it may surprise you to learn that <em>isn’t</em>, in fact, what happens:<sup><a href="#footnote-6" id="footnote-ref-6-1">6</a></sup></p><pre><code>ghci&gt; unsingleton unitList + +error: + • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ + prevents the constraint ‘(UnitList '[a0])’ from being solved. + Probable fix: use a type annotation to specify what ‘a0’ should be. + These potential instances exist: + instance UnitList as =&gt; UnitList (() : as) +</code></pre><p>What went wrong? The type error says that <code>a0</code> is ambiguous, but it only lists a single matching <code>UnitList</code> instance—the one we want—so how can it be ambiguous which one to select?</p><p>The problem stems from the way we defined <code>UnitList</code>. When we wrote the instance</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span></code></pre><p>we said the first element of the type-level list must be <code>()</code>, so there’s nothing stopping someone from coming along and defining another instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="kt">Int</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>In that case, GHC would have no way to know which instance to pick. Nothing in the type of <code>unsingleton</code> forces the element in the list to have type <code>()</code>, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.</p><p>Of course, this isn’t what we want. The <code>UnitList</code> class is supposed to <em>always</em> return a list of <code>()</code> values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Here we’ve changed the instance so that it has the shape <code>UnitList (a ': as)</code>, with a type variable in place of the <code>()</code>, but we also added an equality constraint that forces <code>a</code> to be <code>()</code>. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unsingleton</span><span class="w"> </span><span class="n">unitList</span> +<span class="nb">()</span></code></pre><p>To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="o">&lt;</span><span class="n">constraints</span><span class="o">&gt;</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">C</span><span class="w"> </span><span class="o">&lt;</span><span class="n">types</span><span class="o">&gt;</span></code></pre><p>The part to the left of the <code>=&gt;</code> is known as the <em>instance context</em>, while the part to the right is known as the <em>instance head</em>. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, <strong>only the instance head matters, and the instance context is completely ignored</strong>. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.</p><p>This explains why our two <code>UnitList</code> instances behave differently:</p><ul><li><p>Given the instance head <code>UnitList (() ': as)</code>, GHC won’t select the instance unless it knows the first element of the list is <code>()</code>.</p></li><li><p>But given the instance head <code>UnitList (a ': as)</code>, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.</p></li></ul><p>After the <code>UnitList (a ': as)</code> instance is selected, GHC attempts to solve the constraints in the instance context, including the <code>a ~ ()</code> constraint. This <em>forces</em> <code>a</code> to be <code>()</code>, resolving the ambiguity and allowing type inference to proceed.</p><p>This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:</p><ul><li><p>If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.</p></li><li><p>But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell <em>you</em> what type this is,” effectively giving you a role in type inference itself.</p></li></ul><p>From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.</p><p>Given all of the above, consider again the definition of <code>IsEven</code> from earlier:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an <code>IsEven (a ': b0)</code> constraint, where <code>b0</code> is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an <code>IsEven '[a]</code> instance in the future.</p><p>To fix this, we can apply the same trick we used for <code>UnitList</code>, just in a slightly different way:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Again, the idea is to move the type information we <em>learn</em> from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can <strong>dramatically</strong> improve type inference in programs that make heavy use of TMP.</p><h3><a name="example-3-subtyping-constraints"></a>Example 3: Subtyping constraints</h3><p>At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at <a href="https://hasura.io/">Hasura</a>, I had the opportunity to design an internal parser combinator library that captures aspects of the <a href="https://graphql.org/">GraphQL</a> type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.</p><p>Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our <code>GQLKind</code> datatype has three cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLKind</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Both</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Input</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Output</span></code></pre><p>We use <code>DataKind</code>-promoted versions of this <code>GQLKind</code> type as a parameter to a <code>GQLType</code> GADT:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">TScalar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Both</span> +<span class="w"> </span><span class="kt">TInputObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">InputObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Input</span> +<span class="w"> </span><span class="kt">TIObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Output</span> +<span class="w"> </span><span class="c1">-- ...and so on...</span></code></pre><p>This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type <code>GQLType 'Input</code>, we can’t pass a <code>GQLType 'Both</code>, even though we really ought to be able to.</p><p>To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span></code></pre><p>The first case, <code>KRefl</code>, states that every kind is trivially a subkind of itself. The second case, <code>KBoth</code>, states that <code>Both</code> is a subkind of any kind at all. (This is a particularly literal example of <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">using a type to define axioms</a>.) The next step is to use TMP to implement proof inference:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KBoth</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span></code></pre><p>These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that <code>k</code> is a superkind of <code>'Input</code> or <code>'Output</code>, type inference will force them to be equal.</p><p>Using <code>IsSubKind</code>, we can easily resolve the problem described above. Rather than write a function with a type like this:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>…we simply use an <code>IsSubKind</code> constraint, instead:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now both <code>'Input</code> and <code>'Both</code> kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. <em>Consuming</em> the <code>SubKind</code> proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SelectionSet</span></code></pre><p>This type family is used to determine what a <code>GQLParser k a</code> actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>.</p><p>Fortunately, that is very easy to do using <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html">the <code>(:~:)</code> type from <code>Data.Type.Equality</code> in <code>base</code></a> to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">a</span></code></pre><p>Just as with any other GADT, <code>(:~:)</code> can be used to pack up type equalities and unpack them later; <code>a :~: b</code> just happens to be the GADT that corresponds precisely to the equality <code>a ~ b</code>. Using <code>(:~:)</code>, we can write a reusable proof that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>:</p><pre><code class="pygments"><span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="o">@</span><span class="kt">&#39;Input</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span></code></pre><p>This function is a very simple proof by cases, where <code>Refl</code> can be read as “Q.E.D.”:</p><ul><li><p>In the first case, matching on <code>KRefl</code> refines <code>k</code> to <code>'Input</code>, and <code>ParserInput 'Input</code> is <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li><li><p>Likewise, in the second case, matching on <code>KBoth</code> refines <code>k</code> to <code>'Both</code>, and <code>ParserInput 'Both</code> is also <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li></ul><p>This <code>inputParserInput</code> helper allows functions like <code>nullable</code>, which internally need <code>ParserInput k ~ InputValue</code>, to take the form</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nullable</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">inputParserInput</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ...implementation goes here... -}</span></code></pre><p>Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without <code>IsSubKind</code> doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!</p><h2><a name="wrapping-up-and-closing-thoughts"></a>Wrapping up and closing thoughts</h2><p>So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:</p><ul><li><p>Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.</p></li><li><p>Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.</p></li><li><p>Though I’ve called the technique “<em>typeclass</em> metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.</p></li><li><p>Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.</p></li></ul><p>The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.</p><p>Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.</p><p>It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:</p><ul><li><p>As mentioned earlier in this blog post, <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">the <code>GHC.Generics</code> module documentation</a> is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.</p></li><li><p>I have long believed that <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/">the GHC User’s Guide</a> is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.</p></li><li><p>Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the <a href="https://hackage.haskell.org/package/singletons">singletons</a> library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, <a href="https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf">the accompanying paper</a> is definitely worth a read if you’d like to go down that route.)</p></li></ul><p>Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.</p><ol class="footnotes"><li id="footnote-1"><p>Not to be confused with C++’s <a href="https://en.wikipedia.org/wiki/Template_metaprogramming"><em>template</em> metaprogramming</a>, though there are significant similarities between the two techniques. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>There have been proposals to introduce ordered instances, known in the literature as <a href="https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf"><em>instance chains</em></a>, but as of this writing, GHC does not implement them. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Note that this also preserves an important property of the Haskell type system, parametricity. A function like <code>id :: a -&gt; a</code> shouldn’t be allowed to do different things depending on which type is chosen for <code>a</code>, which our first version of <code>guardUnit</code> tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Short for <em>generalized algebraic datatypes</em>, which is a rather unhelpful name for actually understanding what they are or what they’re for. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for <code>IsEvenTF</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">exists</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">as&#39;</span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">)</span></code></pre><p>The type refinement provided by matching on <code>HCons</code> would be enough for the second case of <code>IsEvenTF</code> to be selected, which would provide an equality proof that <code>as</code> has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. <a href="#footnote-ref-5-1">↩</a></p></li><li id="footnote-6"><p>Actually, I’ve cheated a little bit here, because <code>unsingleton unitList</code> really does typecheck in GHCi under normal circumstances. That’s because <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules">the <code>ExtendedDefaultRules</code> extension</a> is enabled in GHCi by default, which defaults ambiguous type variables to <code>()</code>, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned <code>ExtendedDefaultRules</code> off to illustrate the problem. <a href="#footnote-ref-6-1">↩</a></p></li></ol></article>Names are not type safetyhttps://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/01 Nov 2020<article><p>Haskell programmers spend a lot of time talking about <em>type safety</em>. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published <a href="/blog/2019/11/05/parse-don-t-validate/">Parse, Don’t Validate</a> as an initial stab towards bridging that gap.</p><p>The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s <code>newtype</code> construct. The idea is simple enough—the <code>newtype</code> keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this <em>sounds</em> like a simple and straightforward path to type safety. For example, one might consider using a <code>newtype</code> declaration to define a type for an email address:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This technique can provide <em>some</em> value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct <em>kind</em> of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.</p><p>And names are not type safety.</p><h2><a name="intrinsic-and-extrinsic-safety"></a>Intrinsic and extrinsic safety</h2><p>To illustrate the difference between constructive data modeling (discussed at length in my <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">previous blog post</a>) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">One</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Two</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Three</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Four</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Five</span></code></pre><p>We could then write some functions to convert between <code>Int</code> and our <code>OneToFive</code> type:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">One</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Two</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Three</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Four</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Five</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">3</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">5</span></code></pre><p>This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="kt">Int</span></code></pre><p>Just as before, we can provide <code>toOneToFive</code> and <code>fromOneToFive</code> functions, with identical types:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">otherwise</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="p">(</span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">n</span></code></pre><p>If we put these declarations in their own module and choose not to export the <code>OneToFive</code> constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.</p><p>To see why, suppose we write a function that consumes a <code>OneToFive</code> value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"first"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"second"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"third"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fifth"</span></code></pre><p>The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an <code>Int</code>—after all, it <em>is</em> an <code>Int</code>. An <code>Int</code> can of course contain many other values besides <code>1</code> through <code>5</code>, so we are forced to add an error case to satisfy the exhaustiveness checker:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromOneToFive</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"first"</span> +<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"second"</span> +<span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"third"</span> +<span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fifth"</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: bad OneToFive value"</span></code></pre><p>In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:</p><ul><li><p>The constructive datatype captures its invariants in such a way that they are <em>accessible</em> to downstream consumers. This frees our <code>ordinal</code> function from worrying about handling illegal values, as they have been made unutterable.</p></li><li><p>The newtype wrapper provides a smart constructor that <em>validates</em> the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting <code>Int</code>s.</p></li></ul><p>Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of <code>error</code> has punched a hole right through our type system. If we were to add another constructor to our <code>OneToFive</code> datatype,<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> the version of <code>ordinal</code> that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.</p><p>All of this is a consequence of the fact that the constructive modeling is <em>intrinsically</em> type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent <code>6</code> using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an <code>Int</code>; its meaning is specified extrinsically via the <code>toOneToFive</code> smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.</p><h3><a name="revisiting-non-empty-lists"></a>Revisiting non-empty lists</h3><p>Our <code>OneToFive</code> datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the <code>NonEmpty</code> datatype I’ve repeatedly highlighted in recent blog posts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>It may be illustrative to imagine a version of <code>NonEmpty</code> represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Foldable</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Just as with <code>OneToFive</code>, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for <code>NonEmpty</code> was the ability to write a safe version of <code>head</code>, but the newtype version requires another assertion:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">x</span> +<span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines <code>NonEmpty</code>, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.</p><h2><a name="newtypes-as-tokens"></a>Newtypes as tokens</h2><p>If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes <em>can</em> provide a sort of safety, just a weaker one.</p><p>The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a <em>trust boundary</em> where internal invariants are enforced by restricting clients to a safe API.</p><p>We can use the <code>NonEmpty</code> example from above to illustrate how this works. We refrain from exporting the <code>NonEmpty</code> constructor, and we provide <code>head</code> and <code>tail</code> operations that we trust to never actually fail:</p><pre><code class="pygments"><span class="kr">module</span><span class="w"> </span><span class="nn">Data.List.NonEmpty.Newtype</span> +<span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">NonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">cons</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">nonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">head</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">tail</span> +<span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">cons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span> +<span class="nf">cons</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span> + +<span class="nf">tail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>Since the only way to construct or consume <code>NonEmpty</code> values is to use the functions in <code>Data.List.NonEmpty.Newtype</code>’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like <em>tokens</em>: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case <code>head</code> and <code>tail</code>, to obtain the values contained within.</p><p>This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid <code>NonEmpty []</code> value. For this reason, the newtype approach to type safety does not on its own constitute a <em>proof</em> that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a <code>Generic</code> instance for <code>NonEmpty</code>:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DeriveGeneric #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">GHC.Generics</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span></code></pre><p>However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">GHC</span><span class="o">.</span><span class="kt">Generics</span><span class="o">.</span><span class="n">to</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">K1</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>This is a particularly extreme example, since derived <code>Generic</code> instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived <code>Read</code> instance:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">read</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="s">"NonEmpty []"</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:</p><ul><li><p>All invariants must be made clear to maintainers of the trusted module. For simple types, such as <code>NonEmpty</code>, the invariant is self-evident, but for more sophisticated types, comments are not optional.</p></li><li><p>Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.</p></li><li><p>Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.</p></li><li><p>Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.</p></li></ul><p>In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.</p><p>In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.</p><h2><a name="other-newtype-use-abuse-and-misuse"></a>Other newtype use, abuse, and misuse</h2><p>The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:</p><ul><li><p>Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the <code>Sum</code> and <code>Product</code> newtypes from <code>Data.Monoid</code> provide useful <code>Monoid</code> instances for numeric types.</p></li><li><p>In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The <code>Flip</code> newtype from <code>Data.Bifunctor.Flip</code> is a simple example, flipping the arguments of a <code>Bifunctor</code> so the <code>Functor</code> instance may operate on the other side:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runFlip</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.</p></li><li><p>More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a <code>ByteString</code> containing a secret key may be wrapped in a newtype (with a <code>Show</code> instance omitted) to discourage code from accidentally logging or otherwise exposing it.</p></li></ul><p>All of these applications are good ones, but they have little to do with <em>type safety.</em> The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually <em>prevents</em> misuse; any part of the program may inspect the value at any time.</p><p>Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">unArgumentName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSONKey</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSONKey</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">Hashable</span><span class="p">,</span><span class="w"> </span><span class="kt">ToTxt</span><span class="p">,</span><span class="w"> </span><span class="kt">Lift</span><span class="p">,</span><span class="w"> </span><span class="kt">Generic</span><span class="p">,</span><span class="w"> </span><span class="kt">NFData</span><span class="p">,</span><span class="w"> </span><span class="kt">Cacheable</span><span class="w"> </span><span class="p">)</span></code></pre><p>This newtype is useless noise. Functionally, it is completely interchangeable with its underlying <code>Name</code> type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an <code>ArgumentName</code>, since the enclosing field name already makes its role clear.</p><p>Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:</p><ul><li><p>Primarily, types distinguish <em>functional</em> differences between values. A value of type <code>NonEmpty a</code> is <em>functionally</em> distinct from a value of type <code>[a]</code>, since it is fundamentally structurally different and permits additional operations. In this sense, types are <em>structural</em>; they describe what values <em>are</em> in the internal world of the programming language.</p></li><li><p>Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate <code>Distance</code> and <code>Duration</code> types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.</p></li></ul><p>Note that both these uses are <em>pragmatic</em>; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system <em>is</em> a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like <code>ArgumentName</code>.</p><p>If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use <code>Name</code>, but in situations where the different label adds genuine clarity, one can always use a type alias:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span></code></pre><p>Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.</p><h2><a name="final-thoughts-and-related-reading"></a>Final thoughts and related reading</h2><p>I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.</p><p>Newtypes are one particular mechanism of defining <em>wrapper types</em>, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.</p><p>The catalyst that got me to finally sit down and write this was the recently-published <a href="https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/">Tagged is not a Newtype</a>. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, <code>Tagged</code> <em>is</em> a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.</p><p>Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.</p><p>And a name is not type safety.</p><ol class="footnotes"><li id="footnote-1"><p>Admittedly rather unlikely given its name, but bear with me through the contrived example. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Types as axioms, or: playing god with static typeshttps://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/13 Aug 2020<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more openhttps://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/19 Jan 2020<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Parse, don’t validatehttps://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/05 Nov 2019<article><p>Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.</p><p>However, about a month ago, <a href="https://twitter.com/lexi_lambda/status/1182242561655746560">I was reflecting on Twitter</a> about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:</p><div style="text-align: center; font-size: larger"><strong>Parse, don’t validate.</strong></div><h2><a name="the-essence-of-type-driven-design"></a>The essence of type-driven design</h2><p>Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.</p><h3><a name="the-realm-of-possibility"></a>The realm of possibility</h3><p>One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:</p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Void</span></code></pre><p>Is it possible to implement <code>foo</code>? Trivially, the answer is <em>no</em>, as <code>Void</code> is a type that contains no values, so it’s impossible for <em>any</em> function to produce a value of type <code>Void</code>.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘head’: Patterns not matched: [] +</code></pre><p>This message is helpfully pointing out that our function is <em>partial</em>, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is <code>[]</code>, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.</p><h3><a name="turning-partial-functions-total"></a>Turning partial functions total</h3><p>To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the <code>head</code> function, and we’ll start with the simplest one.</p><h4><a name="managing-expectations"></a>Managing expectations</h4><p>As established, <code>head</code> is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the <code>Maybe</code> type:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span></code></pre><p>This buys us the freedom we need to implement <code>head</code>—it allows us to return <code>Nothing</code> when we discover we can’t produce a value of type <code>a</code> after all:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>Problem solved, right? For the moment, yes… but this solution has a hidden cost.</p><p>Returning <code>Maybe</code> is undoubtably convenient when we’re <em>implementing</em> <code>head</code>. However, it becomes significantly less convenient when we want to actually use it! Since <code>head</code> always has the potential to return <code>Nothing</code>, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">configDirsList</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">configDirsList</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">cacheDir</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="n">cacheDir</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"should never happen; already checked configDirs is non-empty"</span></code></pre><p>When <code>getConfigurationDirectories</code> retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use <code>head</code> in <code>main</code> to get the first element of the list, the <code>Maybe FilePath</code> result still requires us to handle a <code>Nothing</code> case that we know will never happen! This is terribly bad for several reasons:</p><ol><li><p>First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?</p></li><li><p>Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.</p></li><li><p>Finally, and worst of all, this code is a bug waiting to happen! What if <code>getConfigurationDirectories</code> were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update <code>main</code>, and suddenly the “impossible” error becomes not only possible, but probable.</p></li></ol><p>The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically <em>prove</em> the <code>Nothing</code> case impossible, then a modification to <code>getConfigurationDirectories</code> that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.</p><h4><a name="paying-it-forward"></a>Paying it forward</h4><p>Clearly, our modified version of <code>head</code> leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, <code>head</code> should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?</p><p>Let’s look at the original (partial) type signature for <code>head</code> again:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, <code>[a]</code>). Instead of weakening the return type, we can <em>strengthen</em> the argument type, eliminating the possibility of <code>head</code> ever being called on an empty list in the first place.</p><p>To do this, we need a type that represents non-empty lists. Fortunately, the existing <code>NonEmpty</code> type from <code>Data.List.NonEmpty</code> is exactly that. It has the following definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>Note that <code>NonEmpty a</code> is really just a tuple of an <code>a</code> and an ordinary, possibly-empty <code>[a]</code>. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the <code>[a]</code> component is <code>[]</code>, the <code>a</code> component must always be present. This makes <code>head</code> completely trivial to implement:<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Unlike before, GHC accepts this definition without complaint—this definition is <em>total</em>, not partial. We can update our program to use the new implementation:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">FilePath</span><span class="p">)</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">nonEmpty</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="p">)</span></code></pre><p>Note that the redundant check in <code>main</code> is now completely gone! Instead, we perform the check exactly once, in <code>getConfigurationDirectories</code>. It constructs a <code>NonEmpty a</code> from a <code>[a]</code> using the <code>nonEmpty</code> function from <code>Data.List.NonEmpty</code>, which has the following type:</p><pre><code class="pygments"><span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>The <code>Maybe</code> is still there, but this time, we handle the <code>Nothing</code> case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a <code>NonEmpty FilePath</code> value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type <code>NonEmpty a</code> as being like a value of type <code>[a]</code>, plus a <em>proof</em> that the list is non-empty.</p><p>By strengthening the type of the argument to <code>head</code> instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:</p><ul><li><p>The code has no redundant checks, so there can’t be any performance overhead.</p></li><li><p>Furthermore, if <code>getConfigurationDirectories</code> changes to stop checking that the list is non-empty, its return type must change, too. Consequently, <code>main</code> will fail to typecheck, alerting us to the problem before we even run the program!</p></li></ul><p>What’s more, it’s trivial to recover the old behavior of <code>head</code> from the new one by composing <code>head</code> with <code>nonEmpty</code>:</p><pre><code class="pygments"><span class="nf">head&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fmap</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">nonEmpty</span></code></pre><p>Note that the inverse is <em>not</em> true: there is no way to obtain the new version of <code>head</code> from the old one. All in all, the second approach is superior on all axes.</p><h3><a name="the-power-of-parsing"></a>The power of parsing</h3><p>You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:</p><pre><code class="pygments"><span class="nf">validateNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span> + +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="n">xs</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span></code></pre><p>These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: <code>validateNonEmpty</code> always returns <code>()</code>, the type that contains no information, but <code>parseNonEmpty</code> returns <code>NonEmpty a</code>, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but <code>parseNonEmpty</code> gives the caller access to the information it learned, while <code>validateNonEmpty</code> just throws it away.</p><p>These two functions elegantly illustrate two different perspectives on the role of a static type system: <code>validateNonEmpty</code> obeys the typechecker well enough, but only <code>parseNonEmpty</code> takes full advantage of it. If you see why <code>parseNonEmpty</code> is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of <code>parseNonEmpty</code>’s name. Is it really <em>parsing</em> anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe <code>parseNonEmpty</code> is a bona-fide parser (albeit a particularly simple one).</p><p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and <code>parseNonEmpty</code> is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.</p><p>Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:</p><ul><li><p>The <a href="https://hackage.haskell.org/package/aeson">aeson</a> library provides a <code>Parser</code> type that can be used to parse JSON data into domain types.</p></li><li><p>Likewise, <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> provides a set of parser combinators for parsing command-line arguments.</p></li><li><p>Database libraries like <a href="https://hackage.haskell.org/package/persistent">persistent</a> and <a href="https://hackage.haskell.org/package/postgresql-simple">postgresql-simple</a> have a mechanism for parsing values held in an external data store.</p></li><li><p>The <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.</p></li></ul><p>The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.</p><p>One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the <code>NonEmpty</code> example above: if the parsing and processing logic go out of sync, the program will fail to even compile.</p><h3><a name="the-danger-of-validation"></a>The danger of validation</h3><p>Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?</p><p>Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the <a href="http://langsec.org">language-theoretic security</a> field calls <em>shotgun parsing</em>. In the 2016 paper, <a href="http://langsec.org/papers/langsec-cwes-secdev2016.pdf">The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them</a>, its authors provide the following definition:</p><blockquote><p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.</p></blockquote><p>They go on to explain the problems inherent to such validation techniques:</p><blockquote><p>Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.</p></blockquote><p>In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.</p><p>It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.</p><p>Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p><h2><a name="parsing-not-validating-in-practice"></a>Parsing, not validating, in practice</h2><p>So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”</p><p>My advice: focus on the datatypes.</p><p>Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a <code>Map</code>. Adjust your function’s type signature to accept a <code>Map</code> instead of a list of tuples, and implement it as you normally would.</p><p>Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to <code>Map</code>, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of <code>checkNoDuplicateKeys</code>:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span></code></pre><p>Now the check <em>cannot</em> be omitted, since its result is actually necessary for the program to proceed!</p><p>This hypothetical scenario highlights two simple ideas:</p><ol><li><p><strong>Use a data structure that makes illegal states unrepresentable.</strong> Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.</p></li><li><p><strong>Push the burden of proof upward as far as possible, but no further.</strong> Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.</p></li></ol><p>In other words, write functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!</p><p>Here are a handful of additional points of advice, arranged in no particular order:</p><ul><li><p><strong>Let your datatypes inform your code, don’t let your code control your datatypes.</strong> Avoid the temptation to just stick a <code>Bool</code> in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.</p></li><li><p><strong>Treat functions that return <code>m ()</code> with deep suspicion.</strong> Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.</p></li><li><p><strong>Don’t be afraid to parse data in multiple passes.</strong> Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p></li><li><p><strong>Avoid denormalized representations of data, <em>especially</em> if it’s mutable.</strong> Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.</p><ul><li><p><strong>Keep denormalized representations of data behind abstraction boundaries.</strong> If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.</p></li></ul></li><li><p><strong>Use abstract datatypes to make validators “look like” parsers.</strong> Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract <code>newtype</code> with a smart constructor to “fake” a parser from a validator.</p></li></ul><p>As always, use your best judgement. It probably isn’t worth breaking out <a href="https://hackage.haskell.org/package/singletons">singletons</a> and refactoring your entire application just to get rid of a single <code>error "impossible"</code> call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.</p><h2><a name="recap-reflection-and-related-reading"></a>Recap, reflection, and related reading</h2><p>That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.</p><p>None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about <em>process</em>. My hope is that this is a small step in that direction.</p><p>Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post <a href="https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html">Type Safety Back and Forth</a>. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper <a href="https://kataskeue.com/gdp.pdf">Ghosts of Departed Proofs</a>, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.</p><p>As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit <em>any</em> value. These aren’t “real” values (unlike <code>null</code> in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that <a href="https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf">Fast and Loose Reasoning is Morally Correct</a>. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In fact, <code>Data.List.NonEmpty</code> already provides a <code>head</code> function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Empathy and subjective experience in programming languageshttps://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/https://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/19 Oct 2019<article><p>A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.</p><p>Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?</p><p>I think about that question a lot.</p><h2><a name="2015-called-and-they-want-their-dress-back"></a>2015 called, and they want their dress back</h2><p>Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.</p><p>However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into <em>Star Wars</em>. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.</p><p>Take 2015’s phenomenon of “<a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did <em>this</em>—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.</p><h3><a name="when-something-objective-isn-t"></a>When something objective isn’t</h3><p>Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.</p><p>The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.</p><p>In the case of the dress, whether you <a href="https://en.wikipedia.org/wiki/Yanny_or_Laurel">heard “yanny” or “laurel,”</a> or whether you believe the <em>Sonic</em> games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?</p><p>I think the answer is absolutely, unequivocally <em>yes</em>.</p><h2><a name="subjectivity-in-programming-and-in-programming-languages-specifically"></a>Subjectivity in programming, and in programming languages specifically</h2><p>Quick question: which is better, functional or imperative programming?</p><p>My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?</p><p>Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?</p><p>I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> but I did happen to find a link to <a href="https://news.ycombinator.com/item?id=21282647">a recent discussion</a> on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?</p><p>In a <a href="https://news.ycombinator.com/item?id=21284383">branch of the discussion</a>, one user writes:</p><blockquote><blockquote><p>Haskell is great for business and great in production</p></blockquote><p>I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.</p><p>I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.</p></blockquote><p>The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:</p><blockquote><p>I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.</p><p>[…]</p><p>From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.</p></blockquote><p>Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:</p><blockquote><p>Haskell gives one plenty of rope to hang himself on complexity.</p><p>So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.</p></blockquote><p>Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:</p><blockquote><p>Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.</p><p>[…]</p><p>The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.</p><p>Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.</p></blockquote><p>That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?</p><h3><a name="the-unsatisfying-subjective-reality-of-programming-languages"></a>The unsatisfying subjective reality of programming languages</h3><p>You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?</p><p><em><strong>No!</strong></em></p><p>These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, <em>why?</em> Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.</p><p>While <a href="https://news.ycombinator.com/item?id=21284317">one commenter</a> in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” <a href="https://news.ycombinator.com/item?id=21284540">another</a> says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.</p><p>Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t <em>get it.</em> There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.</p><h3><a name="empathy-and-how-bad-results-come-from-good-intentions"></a>Empathy, and how bad results come from good intentions</h3><p>I’ll admit that these kinds of discussions aren’t <em>always</em> fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.</p><p>Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.</p><p>Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:</p><blockquote><p>Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.</p></blockquote><p>I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?</p><p>If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.</p><p>Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.</p><p>To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:</p><blockquote><p>It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.</p><p>Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.</p></blockquote><p>When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective <strong>without</strong> invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about <em>your own</em> experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.</p><p>It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.</p><p>Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be <em>happier.</em></p><ol class="footnotes"><li id="footnote-1"><p>This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is <em>blah</em>,” but you deserve better. So you, uh, get a <em>meta</em> snarky footnote instead. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Which, to be entirely fair, may well be as subjective as anything else in this blog post. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Demystifying MonadBaseControlhttps://lexi-lambda.github.io/blog/2019/09/07/demystifying-monadbasecontrol/https://lexi-lambda.github.io/blog/2019/09/07/demystifying-monadbasecontrol/07 Sep 2019<article><p><a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl"><code>MonadBaseControl</code> from the <code>monad-control</code> package</a> is a confusing typeclass, and its methods have complicated types. For many people, it’s nothing more than scary, impossible-to-understand magic that is, for some reason, needed when lifting certain kinds of operations. Few resources exist that adequately explain how, why, and when it works, which sadly seems to have resulted in some <a href="https://en.wikipedia.org/wiki/Fear,_uncertainty,_and_doubt">FUD</a> about its use.</p><p>There’s no doubt that the machinery of <code>MonadBaseControl</code> is complex, and the role it plays in practice is often subtle. However, its essence is actually much simpler than it appears, and I promise it can be understood by mere mortals. In this blog post, I hope to provide a complete survey of <code>MonadBaseControl</code>—how it works, how it’s designed, and how it can go wrong—in a way that is accessible to anyone with a firm grasp of monads and monad transformers. To start, we’ll motivate <code>MonadBaseControl</code> by reinventing it ourselves.</p><h2><a name="the-higher-order-action-problem"></a>The higher-order action problem</h2><p>Say we have a function with the following type:<sup><a href="#footnote-0" id="footnote-ref-0-1">1</a></sup></p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>If we have an action built from a transformer stack like</p><pre><code class="pygments"><span class="nf">bar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span></code></pre><p>then we might wish to apply <code>foo</code> to <code>bar</code>, but that is ill-typed, since <code>IO</code> is not the same as <code>StateT X IO</code>. In cases like these, we often use <code>lift</code>, but it’s not good enough here: <code>lift</code> <em>adds</em> a new monad transformer to an action, but here we need to <em>remove</em> a transformer. So we need a function with a type like this:</p><pre><code class="pygments"><span class="nf">unliftState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span></code></pre><p>However, if you think about that type just a little bit, it’s clear something’s wrong: it throws away information, namely the state. You may remember that a <code>StateT X IO Y</code> action is equivalent to a function of type <code>X -&gt; IO (Y, X)</code>, so our hypothetical <code>unliftState</code> function has two problems:</p><ol><li><p>We have no <code>X</code> to use as the initial state.</p></li><li><p>We’ll lose any modifications <code>bar</code> made to the state, since the result type is just <code>Y</code>, not <code>(Y, X)</code>.</p></li></ol><p>Clearly, we’ll need something more sophisticated, but what?</p><h2><a name="a-na-ve-solution"></a>A naïve solution</h2><p>Given that <code>foo</code> doesn’t know anything about the state, we can’t easily thread it through <code>foo</code> itself. However, by using <code>runStateT</code> explicitly, we could do some of the state management ourselves:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">v</span></code></pre><p>Do you see what’s going on there? It’s not actually very complicated: we get the current state, then pass it as the initial state to <code>runStateT</code>. This produces an action <code>IO (a, s)</code> that has <em>closed over</em> the current state. We can pass that action to <code>foo</code> without issue, since <code>foo</code> is polymorphic in the action’s return type. Finally, all we have to do is <code>put</code> the modified state back into the enclosing <code>StateT</code> computation, and we can get on with our business.</p><p>That strategy works okay when we only have one monad transformer, but it gets hairy quickly as soon as we have two or more. For example, if we had <code>baz :: ExceptT X (StateT Y IO) Z</code>, then we <em>could</em> do the same trick by getting the underlying</p><pre><code class="pygments"><span class="kt">Y</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">Z</span><span class="p">,</span><span class="w"> </span><span class="kt">Y</span><span class="p">)</span></code></pre><p>function, closing over the state, restoring it, and doing the appropriate case analysis to re-raise any <code>ExceptT</code> errors, but that’s a lot of work to do for every single function! What we’d like to do instead is somehow abstract over the pattern we used to write <code>foo'</code> in a way that scales to arbitrary monad transformers.</p><h2><a name="the-essence-of-monadbasecontrol"></a>The essence of <code>MonadBaseControl</code></h2><p>To build a more general solution for “unlifting” arbitrary monad transformers, we need to start thinking about monad transformer state. The technique we used to implement <code>foo'</code> operated on the following process:</p><ol><li><p>Capture the action’s input state and close over it.</p></li><li><p>Package up the action’s output state with its result and run it.</p></li><li><p>Restore the action’s output state into the enclosing transformer.</p></li><li><p>Return the action’s result.</p></li></ol><p>For <code>StateT s</code>, it turns out that the input state and output state are both <code>s</code>, but other monad transformers have state, too. Consider the input and output state for the following common monad transformers:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>StateT s m a</code></td> + <td><code>s -&gt; m (a, s)</code></td> + <td><code>s</code></td> + <td><code>s</code></td> + </tr> + <tr> + <td><code>ReaderT r m a</code></td> + <td><code>r -&gt; m a</code></td> + <td><code>r</code></td> + <td><code>()</code></td> + </tr> + <tr> + <td><code>WriterT w m a</code></td> + <td><code>m (a, w)</code></td> + <td><code>()</code></td> + <td><code>w</code></td> + </tr> + </table> +</div><p>Notice how the input state is whatever is to the left of the <code>-&gt;</code>, while the output state is whatever extra information gets produced alongside the result. Using the same reasoning, we can also deduce the input and output state for compositions of multiple monad transformers, such as the following:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>ReaderT r (WriterT w m) a</code></td> + <td><code>r -&gt; m (a, w)</code></td> + <td><code>r</code></td> + <td><code>w</code></td> + </tr> + <tr> + <td><code>StateT s (ReaderT r m) a</code></td> + <td><code>r -&gt; s -&gt; m (a, s)</code></td> + <td><code>(r, s)</code></td> + <td><code>s</code></td> + </tr> + <tr> + <td><code>WriterT w (StateT s m) a</code></td> + <td><code>s -&gt; m ((a, w), s)</code></td> + <td><code>s</code></td> + <td><code>(w, s)</code></td> + </tr> + </table> +</div><p>Notice that when monad transformers are composed, their states are composed, too. This is useful to keep in mind, since our goal is to capture the four steps above in a typeclass, polymorphic in the state of the monad transformers we need to lift through. At minimum, we need two new operations: one to capture the input state and close over it (step 1) and one to restore the output state (step 3). One class we might come up with could look like this:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>If we can write instances of that typeclass for various transformers, we can use the class’s operations to implement <code>foo'</code> in a generic way that works with any combination of them:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">v</span></code></pre><p>So how do we implement those instances? Let’s start with <code>IO</code>, since that’s the base case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;&amp;&gt;</span><span class="w"> </span><span class="p">(,</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Not very exciting. The <code>StateT s</code> instance, on the other hand, is significantly more interesting:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(,)</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&lt;*&gt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span></code></pre><p><strong>This instance alone includes most of the key ideas behind <code>MonadBaseControl</code>.</strong> There’s a lot going on, so let’s break it down, step by step:</p><ol><li><p>Start by examining the definitions of <code>InputState</code> and <code>OutputState</code>. Are they what you expected? You’d be forgiven for expecting the following:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">s</span> +<span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">s</span></code></pre><p>After all, that’s what we wrote in the table, isn’t it?</p><p>However, if you give it a try, you’ll find it doesn’t work. <code>InputState</code> and <code>OutputState</code> must capture the state of the <em>entire</em> monad, not just a single transformer layer, so we have to combine the <code>StateT s</code> state with the state of the underlying monad. In the simplest case we get</p><pre><code class="pygments"><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>which is boring, but in a more complex case, we need to get something like this:</p><pre><code class="pygments"><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="kt">IO</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="nb">()</span><span class="p">))</span></code></pre><p>Therefore, <code>InputState (StateT s m)</code> combines <code>s</code> with <code>InputState m</code> in a tuple, and <code>OutputState</code> does the same.</p></li><li><p>Moving on, take a look at <code>captureInputState</code> and <code>closeOverInputState</code>. Just as <code>InputState</code> and <code>OutputState</code> capture the state of the entire monad, these functions need to be inductive in the same way.</p><p><code>captureInputState</code> acquires the current state using <code>get</code>, and it combines it with the remaining monadic state using <code>lift captureInputState</code>. <code>closeOverInputState</code> uses the captured state to peel off the outermost <code>StateT</code> layer, then calls <code>closeOverInputState</code> recursively to peel off the rest of them.</p></li><li><p>Finally, <code>restoreOutputState</code> restores the state of the underlying monad stack, then restores the <code>StateT</code> state, ensuring everything ends up back the way it’s supposed to be.</p></li></ol><p>Take the time to digest all that—work through it yourself if you need to—as it’s a dense piece of code. Once you feel comfortable with it, take a look at the instances for <code>ReaderT</code> and <code>WriterT</code> as well:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(,)</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="o">&lt;*&gt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span></code></pre><p>Make sure you understand these instances, too. It should be easier this time, since they share most of their structure with the <code>StateT</code> instance, but note the asymmetry that arises from the differing input and output states. (It may even help to try and write these instances yourself, focusing on the types whenever you get stuck.)</p><p>If you feel alright with them, then congratulations: you’re already well on your way to grokking <code>MonadBaseControl</code>!</p><h3><a name="hiding-the-input-state"></a>Hiding the input state</h3><p>So far, our implementation of <code>MonadBaseControl</code> works, but it’s actually slightly more complicated than it needs to be. As it happens, all valid uses of <code>MonadBaseControl</code> will always end up performing the following pattern:</p><pre><code class="pygments"><span class="nf">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="kr">let</span><span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span></code></pre><p>That is, we close over the input state as soon as we capture it. We can therefore combine <code>captureInputState</code> and <code>closeOverInputState</code> into a single function:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">))</span></code></pre><p>What’s more, we no longer need the <code>InputState</code> associated type at all! This is an improvement, since it simplifies the API and removes the possibility for any misuse of the input state, since it’s never directly exposed. On the other hand, it has a more complicated type: it produces a monadic action <em>that returns another monadic action</em>. This can be a little more difficult to grok, which is why I presented the original version first, but it may help to consider how the above type arises naturally from the following definition:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">captureInputState</span></code></pre><p>Let’s update the <code>MonadBaseControl</code> class to incorporate this simplification:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>We can then update all the instances to use the simpler API by simply fusing the definitions of <code>captureInputState</code> and <code>closeOverInputState</code> together:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">&lt;&amp;&gt;</span><span class="w"> </span><span class="p">(,</span><span class="w"> </span><span class="nb">()</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span></code></pre><p>This is already very close to a full <code>MonadBaseControl</code> implementation. The <code>captureAndCloseOverInputState</code> implementations are getting a little out of hand, but bear with me—they’ll get simpler before this blog post is over.</p><h3><a name="coping-with-partiality"></a>Coping with partiality</h3><p>Our <code>MonadBaseControl</code> class now works with <code>StateT</code>, <code>ReaderT</code>, and <code>WriterT</code>, but one transformer we haven’t considered is <code>ExceptT</code>. Let’s try to extend our table from before with a row for <code>ExceptT</code>:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>ExceptT e m a</code></td> + <td><code>m (Either e a)</code></td> + <td><code>()</code></td> + <td><code>???</code></td> + </tr> + </table> +</div><p>Hmm… what <em>is</em> the output state for <code>ExceptT</code>?</p><p>The answer can’t be <code>e</code>, since we might not end up with an <code>e</code>—the computation might not fail. <code>Maybe e</code> would be closer… could that work?</p><p>Well, let’s try it. Let’s write a <code>MonadBaseControl</code> instance for <code>ExceptT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Sadly, the above implementation doesn’t typecheck; it is rejected with the following type error:</p><pre><code>• Couldn't match type ‘Either e a’ with ‘(a, Maybe e)’ + Expected type: m (b ((a, Maybe e), OutputState m)) + Actual type: m (b (Either e a, OutputState m)) +• In the second argument of ‘($)’, namely + ‘captureAndCloseOverInputState (runExceptT m)’ + In a stmt of a 'do' block: + m' &lt;- lift $ captureAndCloseOverInputState (runExceptT m) + In the expression: + do m' &lt;- lift $ captureAndCloseOverInputState (runExceptT m) + return do ((v, s'), ss') &lt;- m' + pure (v, (s', ss')) +</code></pre><p>We promised a <code>(a, Maybe e)</code>, but we have an <code>Either e a</code>, and there’s certainly no way to get the former from the latter. Are we stuck? (If you’d like, take a moment to think about how you’d solve this type error before moving on, as it may be helpful for understanding the following solution.)</p><p>The fundamental problem here is <em>partiality</em>. The type of the <code>captureAndCloseOverInputState</code> method always produces an action in the base monad that includes an <code>a</code> <em>in addition</em> to some other output state. But <code>ExceptT</code> is different: when it an error is raised, it doesn’t produce an <code>a</code> at all—it only produces an <code>e</code>. Therefore, as written, it’s impossible to give <code>ExceptT</code> a <code>MonadBaseControl</code> instance.</p><p>Of course, we’d very much <em>like</em> to give <code>ExceptT</code> a <code>MonadBaseControl</code> instance, so that isn’t very satisfying. Somehow, we need to change <code>captureAndCloseOverInputState</code> so that it doesn’t always need to produce an <code>a</code>. There are a few ways we could accomplish that, but an elegant way to do it is this:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>We’ve replaced the old <code>OutputState</code> associated type with a new <code>WithOutputState</code> type, and the key difference between them is that <code>WithOutputState</code> describes the type of a <em>combination</em> of the result (of type <code>a</code>) and the output state, rather than describing the type of the output state alone. For total monad transformers like <code>StateT</code>, <code>ReaderT</code>, and <code>WriterT</code>, <code>WithOutputState m a</code> will just be a tuple of the result value and the output state, the same as before. For example, here’s an updated <code>MonadBaseControl</code> instance for <code>StateT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">a</span></code></pre><p>Before we consider how this helps us with <code>ExceptT</code>, let’s pause for a moment and examine the revised <code>StateT</code> instance in detail, as there are some new things going on here:</p><ul><li><p>Take a close look at the definition of <code>WithOutputState (StateT s m) a</code>. Note that we’ve defined it to be <code>WithOutputState m (a, s)</code>, <em>not</em> <code>(WithOutputState m a, s)</code>. Consider, for a moment, the difference between these types. Can you see why we used the former, not the latter?</p><p>If it’s unclear to you, that’s okay—let’s illustrate the difference with an example. Consider two similar monad transformer stacks:</p><pre><code class="pygments"><span class="nf">m1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="n">a</span> +<span class="nf">m2</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="n">a</span></code></pre><p>Both these stacks contain <code>StateT</code> and <code>ExceptT</code>, but they are layered in a different order. What’s the difference? Well, consider what <code>m1</code> and <code>m2</code> return once fully unwrapped:</p><pre><code class="pygments"><span class="nf">runExceptT</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m1</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))</span> +<span class="nf">runStateT</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m2</span><span class="p">)</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span></code></pre><p>These results are meaningfully different: in <code>m1</code>, the state is discarded if an error is raised, but in <code>m2</code>, the final state is always returned, even if the computation is aborted. What does this mean for <code>WithOutputState</code>?</p><p>Here’s the important detail: <strong>the state is discarded when <code>ExceptT</code> is “inside” <code>StateT</code>, not the other way around.</strong> This can be counterintuitive, since the <code>s</code> ends up <em>inside</em> the <code>Either</code> when the <code>StateT</code> constructor is on the <em>outside</em> and vice versa. This is really just a property of how monad transformers compose, not anything specific to <code>MonadBaseControl</code>, so an explanation of why this happens is outside the scope of this blog post, but the relevant insight is that the <code>m</code> in <code>StateT s m a</code> controls the eventual action’s output state.</p><p>If we had defined <code>WithOutputState (StateT s m) a</code> to be <code>(WithOutputState m a, s)</code>, we’d be in a pickle, since <code>m</code> would be unable to influence the presence of <code>s</code> in the output state. Therefore, we have no choice but to use <code>WithOutputState m (a, s)</code>. (If you are still confused by this, try it yourself; you’ll find that there’s no way to make the other definition typecheck.)</p></li><li><p>Now that we’ve developed an intuitive understanding of why <code>WithOutputState</code> must be defined the way it is, let’s look at things from another perspective. Consider the type of <code>runStateT</code> once more:</p><pre><code class="pygments"><span class="nf">runStateT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span></code></pre><p>Note that the result type is <code>m (a, s)</code>, with the <code>m</code> on the outside. As it happens, this correspondence simplifies the definition of <code>captureAndCloseOverInputState</code>, since we no longer have to do any fiddling with its result—it’s already in the proper shape, so we can just return it directly.</p></li><li><p>Finally, this instance illustrates an interesting change to <code>restoreOutputState</code>. Since the <code>a</code> is now packed inside the <code>WithOutputState m a</code> value, the caller of <code>captureAndCloseOverInputState</code> needs some way to get the <code>a</code> back out! Conveniently, <code>restoreOutputState</code> can play that role, both restoring the output state and unpacking the result.</p><p>Even ignoring partial transformers like <code>ExceptT</code>, this is an improvement over the old API, as it conveniently prevents the programmer from forgetting to call <code>restoreOutputState</code>. However, as we’ll see shortly, it is much more than a convenience: once <code>ExceptT</code> comes into play, it is essential!</p></li></ul><p>With those details addressed, let’s return to <code>ExceptT</code>. Using the new interface, writing an instance for <code>ExceptT</code> is not only possible, it’s actually rather easy:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">either</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span></code></pre><p>This instance illustrates why it’s so crucial that <code>restoreOutputState</code> have the aforementioned dual role: it must handle the case where no <code>a</code> exists at all! In the case of <code>ExceptT</code>, it restores the state in the enclosing monad by re-raising an error.</p><p>Now all that’s left to do is update the other instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">a</span></code></pre><p>Finally, we can update our lifted variant of <code>foo</code> to use the new interface so it will work with transformer stacks that include <code>ExceptT</code>:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span></code></pre><p>At this point, it’s worth considering something: although getting the <code>MonadBaseControl</code> class and instances right was a lot of work, the resulting <code>foo'</code> implementation is actually incredibly simple. That’s a good sign, since we only have to write the <code>MonadBaseControl</code> instances once (in a library), but we have to write functions like <code>foo'</code> quite often.</p><h2><a name="scaling-to-the-real-monadbasecontrol"></a>Scaling to the real <code>MonadBaseControl</code></h2><p>The <code>MonadBaseControl</code> class we implemented in the previous section is complete. It is a working, useful class that is equivalent in power to <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl">the “real” <code>MonadBaseControl</code> class in the <code>monad-control</code> library</a>. However, if you compare the two, you’ll notice that the version in <code>monad-control</code> looks a little bit different. What gives?</p><p>Let’s compare the two classes side by side:</p><pre><code class="pygments"><span class="c1">-- ours</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> + +<span class="c1">-- theirs</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Let’s start with the similarities, since those are easy:</p><ul><li><p>Our <code>WithOutputState</code> associated type is precisely equivalent to their <code>StM</code> associated type, they just use a (considerably) shorter name.</p></li><li><p>Likewise, our <code>restoreOutputState</code> method is precisely equivalent to their <code>restoreM</code> method, simply under a different name.</p></li></ul><p>That leaves <code>captureAndCloseOverInputState</code> and <code>liftBaseWith</code>. Those two methods both do similar things, but they aren’t identical, and that’s where all the differences lie. To understand <code>liftBaseWith</code>, let’s start by inlining the definition of the <code>RunInBase</code> type alias so we can see the fully-expanded type:</p><pre><code class="pygments"><span class="nf">liftBaseWith</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">((</span><span class="n">forall</span><span class="w"> </span><span class="n">c</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">c</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>That type is complicated! However, if we break it down, hopefully you’ll find it’s not as scary as it first appears. Let’s reimplement the <code>foo'</code> example from before using <code>liftBaseWith</code> to show how this version of <code>MonadBaseControl</code> works:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>This is, in some ways, superficially similar to the version we wrote using our version of <code>MonadBaseControl</code>. Just like in our version, we capture the input state, apply <code>foo</code> in the <code>IO</code> monad, then restore the state. But what exactly is doing the state capturing, and what is <code>runInBase</code>?</p><p>Let’s start by adding a type annotation to <code>runInBase</code> to help make it a little clearer what’s going on:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">b</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>That type should look sort of recognizable. If we replace <code>StM</code> with <code>WithOutputState</code>, then we get a type that looks very similar to that of our original <code>closeOverInputState</code> function, except it doesn’t need to take the input state as an argument. How does that work?</p><p>Here’s the trick: <code>liftBaseWith</code> starts by capturing the input state, just as before. However, it then builds a function, <code>runInBase</code>, which is like <code>closeOverInputState</code> partially-applied to the input state it captured. It hands that function to us, and we’re free to apply it to <code>m</code>, which produces the <code>IO (StM m a)</code> action we need, and we can now pass that action to <code>foo</code>. The result is returned in the outer monad, and we restore the state using <code>restoreM</code>.</p><h3><a name="sharing-the-input-state"></a>Sharing the input state</h3><p>At first, this might seem needlessly complicated. When we first started, we separated capturing the input state and closing over it into two separate operations (<code>captureInputState</code> and <code>closeOverInputState</code>), but we eventually combined them so that we could keep the input state hidden. Why does <code>monad-control</code> split them back into two operations again?</p><p>As it turns out, when lifting <code>foo</code>, there’s no advantage to the more complicated API of <code>monad-control</code>. In fact, we could implement our <code>captureAndCloseOverInputState</code> operation in terms of <code>liftBaseWith</code>, and we could use that to implement <code>foo'</code> the same way we did before:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> + +<span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span></code></pre><p>However, that approach has a downside once we need to lift more complicated functions. <code>foo</code> is exceptionally simple, as it only accepts a single input argument, but what if we wanted to lift a more complicated function that took <em>two</em> monadic arguments, such as this one:</p><pre><code class="pygments"><span class="nf">bar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>We could implement that by calling <code>captureAndCloseOverInputState</code> twice, like this:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">ma&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">ma</span> +<span class="w"> </span><span class="n">mb&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">mb</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">bar</span><span class="w"> </span><span class="n">ma&#39;</span><span class="w"> </span><span class="n">mb&#39;</span><span class="p">)</span></code></pre><p>However, that would capture the monadic state twice, which is rather inefficient. By using <code>liftBaseWith</code>, the state capturing is done just once, and it’s shared between all calls to <code>runInBase</code>:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>By providing a “running” function (<code>runInBase</code>) instead of direct access to the input state, <code>liftBaseWith</code> allows sharing the captured input state between multiple actions without exposing it directly.</p><h3><a name="sidebar-continuation-passing-and-impredicativity"></a>Sidebar: continuation-passing and impredicativity</h3><p>One last point before we move on: although the above explains why <code>captureAndCloseOverInputState</code> is insufficient, you may be left wondering why <code>liftBaseWith</code> can’t just <em>return</em> <code>runInBase</code>. Why does it need to be given a continuation? After all, it would be nicer if we could just write this:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">askRunInBase</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">))</span></code></pre><p>To understand the problem with a hypothetical <code>askRunInBase</code> function, remember that the type of <code>runInBase</code> is polymorphic:</p><pre><code class="pygments"><span class="nf">runInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This is important, since if you need to lift a function with a type like</p><pre><code class="pygments"><span class="nf">baz</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)</span></code></pre><p>then you’ll want to instantiate that <code>a</code> variable with two different types. We’d need to retain that power in <code>askRunInBase</code>, so it would need to have the following type:</p><pre><code class="pygments"><span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span></code></pre><p>Sadly, that type is illegal in Haskell. Type constructors must be applied to monomorphic types, but in the above type signature, <code>m</code> is applied to a polymorphic type.<sup><a href="#footnote-1" id="footnote-ref-1-1">2</a></sup> The <code>RankNTypes</code> GHC extension introduces a single exception: the <code>(-&gt;)</code> type constructor is special and may be applied to polymorphic types. That’s why <code>liftBaseWith</code> is legal, but <code>askRunInBase</code> is not: since <code>liftBaseWith</code> is passed a higher-order function that receives <code>runInBase</code> as an argument, the polymorphic type appears immediately under an application of <code>(-&gt;)</code>, which is allowed.</p><p>The aforementioned restriction means we’re basically out of luck, but if you <em>really</em> want <code>askRunInBase</code>, there is a workaround. GHC is perfectly alright with a field of a datatype being polymorphic, so we can define a newtype that wraps a suitably-polymorphic function:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span></code></pre><p>We can now alter <code>askRunInBase</code> to return our newtype, and we can implement it in terms of <code>liftBaseWith</code>:<sup><a href="#footnote-2" id="footnote-ref-2-1">3</a></sup></p><pre><code class="pygments"><span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">runInBase</span></code></pre><p>To use <code>askRunInBase</code>, we have to pattern match on the <code>RunInBase</code> constructor, but it isn’t very noisy, since we can do it directly in a <code>do</code> binding. For example, we could implement a lifted version of <code>baz</code> this way:</p><pre><code class="pygments"><span class="nf">baz&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="nf">baz&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">askRunInBase</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">))</span> +<span class="w"> </span><span class="n">bitraverse</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>As of version 1.0.2.3, <code>monad-control</code> does not provide a newtype like <code>RunInBase</code>, so it also doesn’t provide a function like <code>askRunInBase</code>. For now, you’ll have to use <code>liftBaseWith</code>, but it might be a useful future addition to the library.</p><h2><a name="pitfalls"></a>Pitfalls</h2><p>At this point in the blog post, we’ve covered the essentials of <code>MonadBaseControl</code>: how it works, how it’s designed, and how you might go about using it. However, so far, we’ve only considered situations where <code>MonadBaseControl</code> works well, and I’ve intentionally avoided examples where the technique breaks down. In this section, we’re going to take a look at the pitfalls and drawbacks of <code>MonadBaseControl</code>, plus some ways they can be mitigated.</p><h3><a name="no-polymorphism-no-lifting"></a>No polymorphism, no lifting</h3><p>All of the pitfalls of <code>MonadBaseControl</code> stem from the same root problem, and that’s the particular technique it uses to save and restore monadic state. We’ll start by considering one of the simplest ways that technique is thwarted, and that’s monomorphism. Consider the following two functions:</p><pre><code class="pygments"><span class="nf">poly</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span> +<span class="nf">mono</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">X</span></code></pre><p>Even after all we’ve covered, it may surprise you to learn that although <code>poly</code> can be easily lifted to <code>MonadBaseControl IO m =&gt; m a -&gt; m a</code>, it’s <em>impossible</em> to lift <code>mono</code> to <code>MonadBaseControl IO m =&gt; m X -&gt; m X</code>. It’s a little unintuitive, as we often think of polymorphic types as being more complicated (so surely lifting polymorphic functions ought to be harder), but in fact, it’s the flexibility of polymorphism that allows <code>MonadBaseControl</code> to work in the first place.</p><p>To understand the problem, remember that when we lift a function of type <code>forall a. b a -&gt; b a</code> using <code>MonadBaseControl</code>, we actually instantiate <code>a</code> to <code>(StM m c)</code>. That produces a function of type <code>b (StM m c) -&gt; b (StM m c)</code>, which is isomorphic to the <code>m c -&gt; m c</code> type we want. The instantiation step is easily overlooked, but it’s crucial, since otherwise we have no way to thread the state through the otherwise opaque function we’re trying to lift!</p><p>In the case of <code>mono</code>, that’s exactly the problem we’re faced with. <code>mono</code> will not accept an <code>IO (StM m X)</code> as an argument, only precisely an <code>IO X</code>, so we can’t pass along the monadic state. For all its machinery, <code>MonadBaseControl</code> is no help at all if no polymorphism is involved. Trying to generalize <code>mono</code> without modifying its implementation is a lost cause.</p><h3><a name="the-dangers-of-discarded-state"></a>The dangers of discarded state</h3><p>Our inability to lift <code>mono</code> is frustrating, but at least it’s conclusively impossible. In practice, however, many functions lie in an insidious in-between: polymorphic enough to be lifted, but not without compromises. The simplest of these functions have types such as the following:</p><pre><code class="pygments"><span class="nf">sideEffect</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Unlike <code>mono</code>, it’s entirely possible to lift <code>sideEffect</code>:</p><pre><code class="pygments"><span class="nf">sideEffect&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">sideEffect&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">sideEffect</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>This definition typechecks, but you may very well prefer it didn’t, since it has a serious problem: any changes made by <code>m</code> to the monadic state are completely discarded once <code>sideEffect'</code> returns! Since <code>sideEffect'</code> never calls <code>restoreM</code>, there’s no way the state of <code>m</code> can be any different from the original state, but it’s impossible to call <code>restoreM</code> since we don’t actually get an <code>StM m ()</code> result from <code>sideEffect</code>.</p><p>Sometimes this may be acceptable, since some monad transformers don’t actually have any output state anyway, such as <code>ReaderT r</code>. In other cases, however, <code>sideEffect'</code> could be a bug waiting to happen. One way to make <code>sideEffect'</code> safe would be to add a <code>StM m a ~ a</code> constraint to its context, since that guarantees the monad transformers being lifted through are stateless, and nothing is actually being discarded. Of course, that significantly restricts the set of monad transformers that can be lifted through.</p><h4><a name="rewindable-state"></a>Rewindable state</h4><p>One scenario where state discarding can actually be useful is operations with so-called rewindable or transactional state. The most common example of such an operation is <code>catch</code>:</p><pre><code class="pygments"><span class="nf">catch</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Exception</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>When lifted, state changes from the action <em>or</em> from the exception handler will be “committed,” but never both. If an exception is raised during the computation, those state changes are discarded (“rewound”), giving <code>catch</code> a kind of backtracking semantics. This behavior arises naturally from the way a lifted version of <code>catch</code> must be implemented:</p><pre><code class="pygments"><span class="nf">catch&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Exception</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">catch&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">catch</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>If <code>m</code> raises an exception, it will never return an <code>StM m a</code> value, so there’s no way to get ahold of any of the state changes that happened before the exception. Therefore, the only option is to discard that state.</p><p>This behavior is actually quite useful, and it’s definitely not unreasonable. However, useful or not, it’s inconsistent with state changes to mutable values like <code>IORef</code>s or <code>MVar</code>s (they stay modified whether an exception is raised or not), so it can still be a gotcha. Either way, it’s worth being aware of.</p><h4><a name="partially-discarded-state"></a>Partially discarded state</h4><p>The next function we’re going to examine is <code>finally</code>:</p><pre><code class="pygments"><span class="nf">finally</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function has a similar type to <code>catch</code>, and it even has similar semantics. Like <code>catch</code>, <code>finally</code> can be lifted, but unlike <code>catch</code>, its state <em>can’t</em> be given any satisfying treatment. The only way to implement a lifted version is</p><pre><code class="pygments"><span class="nf">finally&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">finally&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">finally</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>which always discards all state changes made by the second argument. This is clear just from looking at <code>finally</code>’s type: since <code>b</code> doesn’t appear anywhere in the return type, there’s simply no way to access that action’s result, and therefore no way to access its modified state.</p><p>However, don’t despair: there actually <em>is</em> a way to produce a lifted version of <code>finally</code> that preserves all state changes. It can’t be done by lifting <code>finally</code> directly, but if we reimplement <code>finally</code> in terms of simpler lifted functions that are more amenable to lifting, we can produce a lifted version of <code>finally</code> that preserves all the state:<sup><a href="#footnote-3" id="footnote-ref-3-1">4</a></sup></p><pre><code class="pygments"><span class="nf">finally&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">finally&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mask&#39;</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">restore</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">try</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="p">(</span><span class="n">restore</span><span class="w"> </span><span class="n">ma</span><span class="p">))</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">throwIO</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SomeException</span><span class="p">))</span> +<span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">&lt;*</span><span class="w"> </span><span class="n">mb</span></code></pre><p>This illustrates an important (and interesting) point about <code>MonadBaseControl</code>: whether or not an operation can be made state-preserving is not a fundamental property of the operation’s type, but rather a property of the types of the exposed primitives. There is sometimes a way to implement a state-preserving variant of operations that might otherwise seem unliftable given the right primitives and a bit of cleverness.</p><h4><a name="forking-state"></a>Forking state</h4><p>As a final example, I want to provide an example where the state may not actually be discarded <em>per se</em>, just inaccessible. Consider the type of <code>forkIO</code>:</p><pre><code class="pygments"><span class="nf">forkIO</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">ThreadId</span></code></pre><p>Although <code>forkIO</code> isn’t actually polymorphic in its argument, we can convert <em>any</em> <code>IO</code> action to one that produces <code>()</code> via <code>void</code>, so it might as well be. Therefore, we can lift <code>forkIO</code> in much the same way we did with <code>sideEffect</code>:</p><pre><code class="pygments"><span class="nf">forkIO&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">ThreadId</span> +<span class="nf">forkIO&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">forkIO</span><span class="w"> </span><span class="p">(</span><span class="n">void</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>As with <code>sideEffect</code>, we can’t recover the output state, but in this case, there’s a fundamental reason that goes deeper than the types: we’ve forked off a concurrent computation! We’ve therefore split the state in two, which might be what we want… but it also might not. <code>forkIO</code> is yet another illustration that it’s important to think about the state-preservation semantics when using <code>MonadBaseControl</code>, or you may end up with a bug!</p><h2><a name="monadbasecontrol-in-context"></a><code>MonadBaseControl</code> in context</h2><p>Congratulations: you’ve made it through most of this blog post. If you’ve followed everything so far, you now understand <code>MonadBaseControl</code>. All the tricky parts are over. However, before wrapping up, I’d like to add a little extra information about how <code>MonadBaseControl</code> relates to various other parts of the Haskell ecosystem. In practice, that information can be as important as understanding <code>MonadBaseControl</code> itself.</p><h3><a name="the-remainder-of-monad-control"></a>The remainder of <code>monad-control</code></h3><p>If you look at <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html">the documentation for <code>monad-control</code></a>, you’ll find that it provides more than just the <code>MonadBaseControl</code> typeclass. I’m not going to cover everything else in detail in this blog post, but I do want to touch upon it briefly.</p><p>First off, you should definitely take a look at the handful of helper functions provided by <code>monad-control</code>, such as <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:control"><code>control</code></a> and <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:liftBaseOp_"><code>liftBaseOp_</code></a>. These functions provide support for lifting common function types without having to use <code>liftBaseWith</code> directly. It’s useful to understand <code>liftBaseWith</code>, since it’s the most general way to use <code>MonadBaseControl</code>, but in practice, it is simpler and more readable to use the more specialized functions wherever possible. Many of the examples in this very blog post could be simplified using them, and I only stuck to <code>liftBaseWith</code> to introduce as few new concepts at a time as possible.</p><p>Second, I’d like to mention the related <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadTransControl"><code>MonadTransControl</code></a> typeclass. You hopefully remember from earlier in the blog post how we defined <code>MonadBaseControl</code> instances inductively so that we could lift all the way down to the base monad. <code>MonadTransControl</code> is like <code>MonadBaseControl</code> if it intentionally did <em>not</em> do that—it allows lifting through a single transformer at a time, rather than through all of them at once.</p><p>Usually, <code>MonadTransControl</code> is not terribly useful to use directly (though I did use it once <a href="/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/#making-mtls-classes-derivable">in a previous blog post of mine</a> to help derive instances of mtl-style classes), but it <em>is</em> useful for implementing <code>MonadBaseControl</code> instances for your own transformers. If you define a <code>MonadTransControl</code> instance for your monad transformer, you can get a <code>MonadBaseControl</code> implementation for free using the provided <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:ComposeSt"><code>ComposeSt</code></a>, <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultLiftBaseWith"><code>defaultLiftBaseWith</code></a>, and <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultRestoreM"><code>defaultRestoreM</code></a> bindings; see the documentation for more details.</p><h3><a name="lifted-base-and-lifted-async"></a><code>lifted-base</code> and <code>lifted-async</code></h3><p>If you’re going to use <code>MonadBaseControl</code>, the <a href="http://hackage.haskell.org/package/lifted-base"><code>lifted-base</code></a> and <a href="http://hackage.haskell.org/package/lifted-async"><code>lifted-async</code></a> packages are good to know about. As their names imply, they provide lifted versions of bindings in the <code>base</code> and <code>async</code> packages, so you can use them directly without needing to lift them yourself. For example, if you needed a lifted version of <code>mask</code> from <code>Control.Exception</code>, you could swap it for the <code>mask</code> export from <code>Control.Exception.Lifted</code>, and everything would mostly just work (though always be sure to check the documentation for any caveats on state discarding).</p><h3><a name="relationship-to-monadunliftio"></a>Relationship to <code>MonadUnliftIO</code></h3><p>Recently, FP Complete has developed the <a href="https://hackage.haskell.org/package/unliftio"><code>unliftio</code></a> package as an alternative to <code>monad-control</code>. It provides the <a href="https://hackage.haskell.org/package/unliftio-core-0.1.2.0/docs/Control-Monad-IO-Unlift.html#t:MonadUnliftIO"><code>MonadUnliftIO</code></a> typeclass, which is similar in spirit to <code>MonadBaseControl</code>, but heavily restricted: it is specialized to <code>IO</code> as the base monad, and it <em>only</em> allows instances for stateless monads, such as <code>ReaderT</code>. This is designed to encourage the so-called <a href="https://www.fpcomplete.com/blog/2017/06/readert-design-pattern"><code>ReaderT</code> design pattern</a>, which avoids ever using stateful monads like <code>ExceptT</code> or <code>StateT</code> over <code>IO</code>, encouraging the use of <code>IO</code> exceptions and mutable variables (e.g. <code>MVar</code>s or <code>TVar</code>s) instead.</p><p>I should be clear: I really like most of what FP Complete has done—to this day, I still use <code>stack</code> as my Haskell build tool of choice—and I think the suggestions given in the aforementioned “<code>ReaderT</code> design pattern” blog post have real weight to them. I have a deep respect for Michael Snoyman’s commitment to opinionated, user-friendly tools and libraries. But truthfully, I can’t stand <code>MonadUnliftIO</code>.</p><p><code>MonadUnliftIO</code> is designed to avoid all the complexity around state discarding that <code>MonadBaseControl</code> introduces, and on its own, that’s a noble goal. Safety first, after all. The problem is that <code>MonadUnliftIO</code> really is extremely limiting, and what’s more, it can actually be trivially encoded in terms of <code>MonadBaseControl</code> as follows:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">MonadUnliftIO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This alias can be used to define safe, lifted functions that never discard state while still allowing functions that <em>can</em> be safely lifted through stateful transformers to do so. Indeed, the <a href="https://hackage.haskell.org/package/lifted-async-0.10.0.4/docs/Control-Concurrent-Async-Lifted-Safe.html"><code>Control.Concurrent.Async.Lifted.Safe</code></a> module from <code>lifted-async</code> does exactly that (albeit with a slightly different formulation than the above alias).</p><p>To be fair, the <code>unliftio</code> README does address this in its <a href="https://github.com/fpco/unliftio/tree/bb2e26e7fbbaebb15555f417ba9753a76b3218b2/unliftio#monad-control">comparison section</a>:</p><blockquote><p><code>monad-control</code> allows us to unlift both styles. In theory, we could write a variant of <code>lifted-base</code> that never does state discards […] In other words, this is an advantage of <code>monad-control</code> over <code>MonadUnliftIO</code>. We've avoided providing any such extra typeclass in this package though, for two reasons:</p><ul><li><p><code>MonadUnliftIO</code> is a simple typeclass, easy to explain. We don't want to complicated [sic] matters […]</p></li><li><p>Having this kind of split would be confusing in user code, when suddenly [certain operations are] not available to us.</p></li></ul></blockquote><p>In other words, the authors of <code>unliftio</code> felt that <code>MonadBaseControl</code> was simply not worth the complexity, and they could get away with <code>MonadUnliftIO</code>. Frankly, if you feel the same way, by all means, use <code>unliftio</code>. I just found it too limiting given the way I write Haskell, plain and simple.</p><h2><a name="recap"></a>Recap</h2><p>So ends another long blog post. As often seems the case, I set out to write something short, but I ended up writing well over 5,000 words. I suppose that means I learned something from this experience, too: <code>MonadBaseControl</code> is more complicated than I had anticipated! Maybe there’s something to take away from that.</p><p>In any case, it’s over now, so I’d like to briefly summarize what we’ve covered:</p><ul><li><p><a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl"><code>MonadBaseControl</code></a> allows us to lift higher-order monadic operations.</p></li><li><p>It operates by capturing the current monadic state and explicitly threading it through the action in the base monad before restoring it.</p></li><li><p>That technique works well for polymorphic operations for the type <code>forall a. b a -&gt; b a</code>, but it can be tricky or even impossible for more complex operations, sometimes leading to discarded state.</p><p>This can sometimes be mitigated by restricting certain operations to stateless monads using a <code>StM m a ~ a</code> constraint, or by reimplementing the operation in terms of simpler primitives.</p></li><li><p>The <a href="http://hackage.haskell.org/package/lifted-base"><code>lifted-base</code></a> and <a href="http://hackage.haskell.org/package/lifted-async"><code>lifted-async</code></a> packages provide lifted versions of existing operations, avoiding the need to lift them yourself.</p></li></ul><p>As with many abstractions in Haskell, don’t worry too much if you don’t have a completely firm grasp of <code>MonadBaseControl</code> at first. Insight often comes with repeated experience, and <code>monad-control</code> can still be used in useful ways even without a perfect understanding. My hope is that this blog post has helped you build intuitions about <code>MonadBaseControl</code> even if some of the underlying machinery remains a little fuzzy, and I hope it can also serve as a reference for those who want or need to understand (or just be reminded of) all the little details.</p><p>Finally, I’ll admit <code>MonadBaseControl</code> isn’t especially elegant or beautiful as Haskell abstractions go. In fact, in many ways, it’s a bit of a kludge! Perhaps, in time, effect systems will evolve and mature so that it and its ilk are no longer necessary, and they may become distant relics of an inferior past. But in the meantime, it’s here, it’s useful, and I think it’s worth embracing. If you’ve shied away from it in the past, I hope I’ve illuminated it enough to make you consider giving it another try.</p><ol class="footnotes"><li id="footnote-0"><p>One example of a function with that type is <code>mask_</code>. <a href="#footnote-ref-0-1">↩</a></p></li><li id="footnote-1"><p>Types with polymorphic types under type constructors are called <em>impredicative</em>. GHC technically has limited support for impredicativity via the <code>ImpredicativeTypes</code> language extension, but as of GHC 8.8, it has been fairly broken for some time. A fix is apparently being worked on, but even if that effort is successful, I don’t know what impact it will have on type inference. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Note that <code>askRunInBase = liftBaseWith (pure . RunInBase)</code> does <em>not</em> typecheck, as it would require impredicative polymorphism: it would require instantiating the type of <code>(.)</code> with polymorphic types. The version using <code>($)</code> works because GHC actually has special typechecking rules for <code>($)</code>! Effectively, <code>f $ x</code> is really syntax in GHC. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Assume that <code>mask'</code> is a suitably lifted version of <code>mask</code> (which can in fact be made state-preserving). <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Defeating Racket’s separate compilation guaranteehttps://lexi-lambda.github.io/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/https://lexi-lambda.github.io/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/21 Apr 2019<article><p>Being a self-described <a href="https://felleisen.org/matthias/manifesto/sec_pl-pl.html">programming-language programming language</a> is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate <a href="https://www.cs.utah.edu/plt/publications/macromod.pdf">composable and compilable macros</a>. One of the module system’s foundational properties is its <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29"><em>separate compilation guarantee</em></a>, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is <em>essential</em> for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.</p><p>Yet today, in this blog post, we’re going to do exactly that.</p><h2><a name="what-is-the-separate-compilation-guarantee"></a>What is the separate compilation guarantee?</h2><p>Before we get to the fun part (i.e. breaking things), let’s go over some fundamentals so we understand what we’re breaking. The authoritative source for the separate compilation guarantee is <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">the Racket reference</a>, but it is dense, as authoritative sources tend to be. Although I enjoy reading technical manuals for sport, it is my understanding that not all the people who read this blog are as strange as I am, so let’s start with a quick primer, instead. (If you’re already an expert, feel free to <a href="#section:main-start">skip to the next section</a>.)</p><p>Racket is a macro-enabled programming language. In Racket, a macro is a user-defined, code-to-code transformation that occurs at compile-time. These transformations cannot make arbitrary changes to the program—in Racket, they are usually required to be <em>local</em>, affecting a single expression or definition at a time—but they may be implemented using arbitrary code. This means that a macro can, if it so desires, read the SSH keys off your filesystem and issue an HTTP request to send them someplace.</p><p>That kind of attack is bad, admittedly, but it’s also <em>uninteresting</em>: Racket allows you do all that and then some, making no attempt to prevent it.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Racket calls these “external effects,” things that affect state outside of the programming language. They sound scary, but in practice, <em>internal effects</em>—effects that mutate state inside the programming language—are a much bigger obstacle to practical programming. Let’s take a look at why.</p><p>Let’s say we have a module with some global, mutable state. Perhaps it is used to keep track of a set of delicious foods:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><p>Using this interface, let’s write a program that checks if a particular food, given as a command-line argument, is delicious:</p><pre><code class="pygments"><span class="c1">;; check-food.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"pineapple"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"sushi"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"cheesecake"</span><span class="p">)</span> + +<span class="p">(</span><span class="k">command-line</span> +<span class="w"> </span><span class="kd">#:args</span><span class="w"> </span><span class="p">[</span><span class="n">food-to-check</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is not delicious.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>cheesecake +cheesecake<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>licorice +licorice<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>Exhilarating. (Sorry, licorice fans.) But what if a <em>macro</em> were to call <code>add-delicious-food!</code>? What would happen? For example, what if we wrote a macro to add a lot of foods at once?<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="n">fst:string</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd:string</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">for*</span><span class="w"> </span><span class="p">([</span><span class="n">fst-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">fst</span><span class="w"> </span><span class="k">...</span><span class="p">]))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">snd</span><span class="w"> </span><span class="k">...</span><span class="p">]))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="p">(</span><span class="nb">string-append</span><span class="w"> </span><span class="n">fst-str</span><span class="w"> </span><span class="s2">" "</span><span class="w"> </span><span class="n">snd-str</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">))</span> + +<span class="c1">; should add “fried chicken,” “roasted chicken”, “fried potato,” and “roasted potato”</span> +<span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="s2">"fried"</span><span class="w"> </span><span class="s2">"roasted"</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="s2">"chicken"</span><span class="w"> </span><span class="s2">"potato"</span><span class="p">])</span></code></pre><p>Now, what do you think executing <code>racket check-food.rkt 'fried chicken'</code> will do?</p><p>Clearly, the program should print <code>fried chicken is a delicious food</code>, and indeed, many traditional Lisp systems would happily produce such a result. After all, running <code>racket check-food.rkt 'fried chicken'</code> must load the source code inside <code>check-food.rkt</code>, expand and compile it, then run the result. While the program is being expanded, the compile-time calls to <code>add-delicious-food!</code> should add new elements to the <code>delicious-food</code> set, so when the program is executed, the string <code>"fried chicken"</code> ought to be in it.</p><p>But if you actually try this yourself, you will find that <em>isn’t</em> what happens. Instead, Racket rejects the program:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +check-food.rkt:12:11:<span class="w"> </span>add-delicious-food!:<span class="w"> </span>reference<span class="w"> </span>to<span class="w"> </span>an<span class="w"> </span>unbound<span class="w"> </span>identifier +<span class="w"> </span>at<span class="w"> </span>phase:<span class="w"> </span><span class="m">1</span><span class="p">;</span><span class="w"> </span>the<span class="w"> </span>transformer<span class="w"> </span>environment +<span class="w"> </span><span class="k">in</span>:<span class="w"> </span>add-delicious-food!</code></pre><p>Why does Racket reject this program? Well, consider that Racket allows programs to be pre-compiled using <code>raco make</code>, doing all the work of macroexpansion and compilation to bytecode ahead of time. Subsequent runs of the program will use the pre-compiled version, without having to run all the macros again. This is a problem, since expanding the <code>add-food-combinations!</code> macro had side-effects that our program depended on!</p><p>If Racket allowed the above program, it might do different things depending on whether it was pre-compiled. Running directly from source code might treat <code>'fried chicken'</code> as a delicious food, while running from pre-compiled bytecode might not. Racket considers this unacceptable, so it disallows the program entirely.</p><h3><a name="preserving-separate-compilation-via-phases"></a>Preserving separate compilation via phases</h3><p>Hopefully, you are now mostly convinced that the above program is a bad one, but you might have some lingering doubts. You might, for example, wonder if Racket disallows mutable compile-time state entirely. That is not the case—Racket really does allow everything that happens at runtime to happen at compile-time—but it does prevent compile-time and run-time state from ever <em>interacting</em>. Racket stratifies every program into a compile-time part and a run-time part, and it restricts communication between them to limited, well-defined channels (mainly via expanding to code that does something at run-time).</p><p>Racket calls this system of stratification <em>phases</em>. Code that executes at run-time belongs to the <em>run-time phase</em>, while code that executes at compile-time (i.e. macros) belongs to the <em>compile-time phase</em>. When a variable is defined, it is always defined in a particular phase, so bindings declared with <code>define</code> can only be used at run-time, while bindings declared with <code>define-for-syntax</code> can only be used at compile-time. Since <code>add-delicious-food!</code> was declared using <code>define</code>, it was not allowed (and in fact was not even visible) in the body of the <code>add-food-combinations!</code> macro.</p><p>While the whole macro system could work precisely as just described, such a strict stratification would be incredibly rigid. Since every definition would belong to either run-time or compile-time, but never both, reusing run-time code to implement macros would be impossible. While the example in the previous section might make it seem like that’s a good thing, it very often isn’t: imagine if general-purpose functions like <code>map</code> and <code>filter</code> all needed to be written twice!</p><p>To avoid this problem, Racket allows modules to be imported at both run-time and compile-time, so long as it’s done explicitly. Writing <code>(require "some-library.rkt")</code> requires <code>some-library.rkt</code> for run-time code, but writing <code>(require (for-syntax "some-library.rkt"))</code> requires it for compile-time code. Requiring a module <code>for-syntax</code> is sort of like implicitly adjusting all of its uses of <code>define</code> to be <code>define-for-syntax</code>, instead, effectively shifting all the code from run-time to compile-time. This kind of operation is therefore known as <em>phase shifting</em> in Racket terminology.</p><p>We can use phase shifting to make the program we wrote compile. If we adjust the <code>require</code> at the beginning of our program, then we can ensure <code>add-delicious-food!</code> is visible to both the run-time and compile-time parts of <code>check-food.rkt</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">))</span></code></pre><p>Now our program compiles. However, if you’ve been following everything carefully, you should be wondering why! According to the last section, sharing state between run-time and compile-time fundamentally can’t work without introducing inconsistencies between uncompiled and pre-compiled code. And that’s true—such a thing would cause all sorts of problems, and Racket doesn’t allow it. If you run the program, whether pre-compiled or not, you’ll find it always does the same thing:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>This seems rather confusing. What happened to the calls to <code>add-delicious-food!</code> inside our <code>add-food-combinations!</code> macro? If we stick a <code>printf</code> inside <code>add-delicious-food!</code>, we’ll find that it really does get called:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"Registering ~a as a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And in fact, if we pre-compile <code>check-food.rkt</code>, we’ll see that the first four registrations appear at compile-time, exactly as we expect:</p><pre><code class="pygments">$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>The compile-time registrations really are happening, but Racket is automatically restricting the compile-time side-effects so they only apply at compile-time. After compilation has finished, Racket ensures that compile-time side effects are thrown away, and the run-time code starts over with fresh, untouched state. This guarantees consistent behavior, since it becomes impossible to distinguish at run-time whether a module was just compiled on the fly, or if it was pre-compiled long ago (possibly even on someone else’s computer).</p><p>This is the essence of the separate compilation guarantee. To summarize:</p><ul><li><p>Run-time and compile-time are distinct <em>phases</em> of execution, which cannot interact.</p></li><li><p>Modules can be required at multiple phases via <em>phase shifting</em>, but their state is kept separate. Each phase gets its own copy of the state.</p></li><li><p>Ensuring that the state is kept separate ensures predictable program behavior, no matter when the program is compiled.</p></li></ul><p>This summary is a simplification of phases in Racket. The full Racket module system does not have only two phases, since macros can also be <em>used</em> at compile-time to implement other macros, effectively creating a separate “compile-time” for the compile-time code. Each compile-time pass is isolated to its own phase, creating a finite but arbitrarily large number of distinct program phases (all but one of which occur at compile-time).</p><p>Furthermore, the separate compilation guarantee does not just isolate the state of each phase from the state of other phases but also isolates all compile-time state from the compile-time state of other modules. This ensures that compilation is still deterministic even if modules are compiled in a different <em>order</em>, or if several modules are sometimes compiled individually while other times compiled together all at once.</p><p>If you want to learn more, the full details of the module system are described at length in the <a href="https://docs.racket-lang.org/guide/phases.html">General Phase Levels</a> section of the Racket Guide, but the abridged summary I’ve described is enough for the purposes of this blog post. If the bulleted list above mostly made sense to you, you’re ready to move on.</p><h2 id="section:main-start">How we’re going to break it</h2><p>The separate compilation guarantee is a sturdy opponent, but it is not without weaknesses. Although no API in Racket, safe or unsafe, allows arbitrarily disabling phase separation, a couple features of Racket are already known to allow limited forms of cross-phase communication.</p><p>The most significant of these, and the one we’ll be using as our vector of attack, is the logger. Unlike many logging systems, which are exclusively string-oriented, Racket’s logging interface allows structured logging by associating an arbitrary Racket value with each and every log message. Since it is possible to set up listeners within Racket that receive log messages sent to a particular “topic,” the logger can be used as a communication channel to send values between different parts of a program.</p><p>The following program illustrates how this works. One thread creates a listener for all log messages on the topic <code>'send-me-a-value</code> using <code>make-log-receiver</code>, then uses <code>sync</code> to block until a value is received. Meanwhile, a second thread sends values through the logger using <code>log-message</code>. Together, this creates a makeshift buffered, asynchronous channel:</p><pre><code class="pygments"><span class="c1">;; log-comm.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span><span class="w"> </span><span class="c1">; wait forever</span></code></pre><pre><code>$ racket log-comm.rkt +'#(debug "" 1 send-me-a-value) +'#(debug "" 2 send-me-a-value) +'#(debug "" 3 send-me-a-value) +'#(debug "" 4 send-me-a-value) +^Cuser break +</code></pre><p>In this program, the value being sent through the logger is just a number, which isn’t very interesting. But the value really can be <em>any</em> value, even arbitrary closures or mutable data structures. It’s even possible to send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> through a logger, which can subsequently be used to communicate directly, without having to abuse the logger.</p><p>Generally, this feature of loggers isn’t very useful, since Racket has plenty of features for cross-thread communication. What’s special about the logger, however, is that it is global, and it is cross-phase.</p><p>The cross-phase nature of the logger makes some sense. If a Racket program creates a namespace (that is, a fresh environment for dynamic evaluation), then uses it to expand and compile a Racket module, the process of compilation might produce some log messages, and the calling thread might wish to receive them. It wouldn’t be a very useful logging system if log messages during compile-time were always lost. However, this convenience is a loophole in the phase separation system, since it allows values to flow—bidirectionally—between phases.</p><p>This concept forms the foundation of our exploit, but it alone is not a new technique, and I did not discover it. However, all existing uses I know of that use the logger for cross-phase communication require control of the parent namespace in which modules are being compiled, which means some code must exist “outside” the actual program. That technique does not work for ordinary programs run directly with <code>racket</code> or compiled directly with <code>raco make</code>, so to get there, we’ll need something more clever.</p><h3><a name="the-challenge"></a>The challenge</h3><p>Our goal, therefore, is to share state between phases <em>without</em> controlling the compilation namespace. More precisely, we want to be able to create an arbitrary module-level definition that is <em>cross-phase persistent</em>, which means it will be evaluated once and only once no matter how many times its enclosing module is re-instantiated (i.e. given a fresh, untouched state) at various phases. A phase-shifted <code>require</code> of the module that contains the definition should share state with an unshifted version of the module, breaking the separate compilation guarantee wide open.</p><p>To use the example from the previous section, we should be able to adjust <code>foods.rkt</code> very slightly…</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="c1">; share across phases</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="cm">#| ... |#</span></code></pre><p>…and the <code>delicious-foods</code> mutable state should magically become cross-phase persistent. When running <code>check-food.rkt</code> from source, we should see the side-effects persisted from the module’s compilation, while running from pre-compiled bytecode should give us the result with compile-time effects discarded.</p><p>We already know the logger is going to be part of our exploit, but implementing <code>define/cross-phase</code> on top of it is more subtle than it might seem. In our previous example that used <code>make-log-receiver</code>, we had well-defined sender and receiver threads, but who is the “sender” in our multi-phased world? And what exactly is the sender sending?</p><p>To answer those questions, allow me to outline the general idea of our approach:</p><ol><li><p>The first time our <code>foods.rkt</code> module is instantiated, at any phase, it evaluates the <code>(mutable-set)</code> expression to produce a new mutable set. It spawns a sender thread that sends this value via the logger to anyone who will listen, and that thread lingers in the background for the remaining duration of the program.</p></li><li><p>All subsequent instantiations of <code>foods.rkt</code> do <em>not</em> evaluate the <code>(mutable-set)</code> expression. Instead, they obtain the existing set by creating a log receiver and obtaining the value the sender thread is broadcasting. This ensures that a single value is shared across all instantiations of the module.</p></li></ol><p>This sounds deceptively simple, but the crux of the problem is how to determine whether <code>foods.rkt</code> has previously been instantiated or not. Since we can only communicate across phases via the logger, we cannot use any shared state to directly record the first time the module is instantiated. We can listen to a log receiver and wait to see if we get a response, but this introduces a race condition: how long do we wait until giving up and deciding we’re the first instantiation? Worse, what if two threads instantiate the module at the same time, and both threads end up spawning a new sender thread, duplicating the state?</p><p>The true challenge, therefore, is to develop a protocol by which we can be <em>certain</em> we are the first instantiation of a module, without relying on any unspecified behavior, and without introducing any race conditions. This is possible, but it isn’t obvious, and it requires combining loggers with some extra tools available to the Racket programmer.</p><h3><a name="the-key-idea"></a>The key idea</h3><p>It’s finally time to tackle the key idea at the heart of our exploit: garbage collection. In Racket, garbage collection is an observable effect, since Racket allows attaching finalizers to values via <a href="https://docs.racket-lang.org/reference/willexecutor.html">wills and executors</a>. Since a single heap is necessarily shared by the entire VM, behavior happening on other threads (even in other phases) can be indirectly observed by creating a unique value—a “canary”—then sending it to another thread, and waiting to see if it will be garbage collected or not (that is, whether or not the canary “dies”).</p><p>Remember that logs and log receivers are effectively buffered, multicast, asynchronous FIFO channels. Since they are buffered, if any thread is already listening to a logger topic when a value is sent, it cannot possibly be garbage collected until that thread either reads it and discards it or the receiver itself is garbage collected. It’s possible to use this mechanism to observe whether or not another thread is already listening on a topic, as the following program demonstrates:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">executor</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><pre><code>$ racket check-receivers.rkt +no receivers for 'bar +receiver exists for 'foo +</code></pre><p>However, this program has some problems. For one, it needs to call <code>collect-garbage</code> several times to be certain that the canary will be collected if there are no listeners, which can take a second or two, and it also assumes that three calls to <code>collect-garbage</code> will be enough to collect the canary, though there is no guarantee that will be true.</p><p>A bulletproof solution should be both reasonably performant and guaranteed to work. To get there, we have to combine this idea with something more. Here’s the trick: instead of sending the canary alone, send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> alongside it. Synchronize on both the canary’s executor <em>and</em> the channel so that the thread will unblock if either the canary is collected <em>or</em> the channel is received and sent a value using <code>channel-put</code>. Have the receiver listen for the channel on a separate thread, and when it receives it, send a value back to unblock the waiting thread as quickly as possible, without needing to rely on a timeout or a particular number of calls to <code>collect-garbage</code>.</p><p>Using that idea, we can revise the program:</p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="c1">; send the channel + the canary</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">canary</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span><span class="w"> </span><span class="c1">; yield to try to let the receiver thread work</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">received</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">)))</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">received</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span><span class="w"> </span><span class="c1">; collect garbage and try again</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> +<span class="p">(</span><span class="nb">void</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="k">_</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><p>Now the program completes almost instantly. For this simple program, the explicit <code>(sleep)</code> call is effective enough at yielding that, on my machine, <code>(check-receivers 'foo)</code> returns without ever calling <code>collect-garbage</code>, and <code>(check-receivers 'bar)</code> returns after performing a single minor collection.</p><p>This is extremely close to a bulletproof solution, but there are two remaining subtle issues:</p><ol><li><p>There is technically a race condition between the <code>(sync recv)</code> in the receiver thread and the subsequent <code>channel-put</code>, since it’s possible for the canary to be received, discarded, and garbage collected before reaching the call to <code>channel-put</code>, which the sending thread would incorrectly interpret as indicating the topic has no receivers.</p><p>To fix that, the receiver thread can send the canary itself back through the channel, which fundamentally has to work, since the value cannot be collected until it has been received by the sending thread, at which point the <code>sync</code> has already chosen the channel.</p></li><li><p>It is possible for the receiver thread to receive the log message and call <code>channel-put</code>, but for the sending thread to somehow die in the meantime (which cannot be protected against in general in Racket, since <code>thread-kill</code> immediately and forcibly terminates a thread). If this were to happen, the sending thread would never obtain the value from the channel, blocking the receiving thread indefinitely.</p><p>A solution is to spawn a new thread for each <code>channel-put</code> instead of calling it directly from the receiving thread. Conveniently, this both ensures the receiving thread never gets stuck and avoids resource leaks, since the Racket runtime is smart enough to GC a thread blocked on a channel that has no other references and therefore can never be unblocked.</p></li></ol><p>With those fixes in place, the program is, to the best of my knowledge, bulletproof. It will always correctly determine whether or not a logger has a listener, with no race conditions or reliance upon unspecified behavior of the Racket runtime. It does, however, make a couple of assumptions.</p><p>First, it assumes that the value of <code>(current-logger)</code> is shared between the threads. It is true that <code>(current-logger)</code> can be changed, and sometimes is, but it’s usually done via <code>parameterize</code>, not mutation of the parameter directly. Therefore, this can largely be mitigated by storing the value of <code>(current-logger)</code> at module instantiation time.</p><p>Second, it assumes that no other receivers are listening on the same topic. Technically, even using a unique, uninterned key for the topic is insufficient to ensure that no receivers are listening to it, since a receiver can choose to listen to all topics. However, in practice, it is highly unlikely that any receiver would willfully choose to listen to all topics at the <code>'debug</code> level, since the receiver would be inundated with enormous amounts of useless information. Even if such a receiver were to be created, it is highly likely that it would dequeue the messages as quickly as possible and discard the accompanying payload, since doing otherwise would cause all messages to be retained in memory, leading to a significant memory leak.</p><p>Both these problems can be mitigated by using a logger other than the root logger, which is easy in this example. However, for the purpose of subverting the separate compilation guarantee, we would have no way to share the logger object itself across phases, defeating the whole purpose, so we are forced to use the root logger and hope the above two assumptions remain true (but they usually do).</p><h3><a name="preparing-the-exploit"></a>Preparing the exploit</h3><p>If you’ve made it here, congratulations! The most difficult part of this blog post is over. All that’s left is the fun part: performing the exploit.</p><p>The bulk of our implementation is a slightly adapted version of <code>check-receivers</code>:</p><pre><code class="pygments"><span class="c1">;; define-cross-phase.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="k">thunk</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="k">==</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="nb">eq?</span><span class="p">))</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">execute-evt</span><span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="nb">will-execute</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">execute-evt</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">result</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">value</span><span class="p">)</span> +<span class="w"> </span><span class="n">value</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="p">(</span><span class="k">thunk</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">value</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)]))))</span> +<span class="w"> </span><span class="n">value</span><span class="p">]))</span></code></pre><p>There are a few minor differences, which I’ll list:</p><ol><li><p>The most obvious difference is that <code>make-cross-phase</code> does the work of both checking if a receiver exists—which I’ll call the <em>manager thread</em>—and spawning it if it doesn’t. If it does end up spawning a manager thread, it evaluates the given thunk to produce a value, which becomes the cross-phase value that will be sent through the channel alongside the canary.</p></li><li><p>Once the manager thread is created, subsequent calls to <code>make-cross-phase</code> will receive the value through the channel and return it instead of re-invoking <code>thunk</code>. This is what ensures the right-hand side of each use of <code>define/cross-phase</code> is only ever evaluated once.</p></li><li><p>Since <code>make-cross-phase</code> needs to create a log receiver if no manager thread exists, it does so immediately, before sending the canary through the logger. This avoids a race condition between multiple threads that are simultaneously competing to become the manager thread, where both threads could send a canary through the logger before either was listening, both canaries would get GC’d, and both threads would spawn a new manager.<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><p>Creating the receiver before sending the canary avoids this problem, but the thread now needs to receive its own canary and discard it before synchronizing on the channel and executor, since otherwise it will retain a reference to the canary. It’s possible that in between creating the receiver and sending the canary, another thread also sent a canary, so it needs to drop any log messages it finds that don’t include its own canary.</p><p>This ends up working out perfectly, since every thread drops all the messages received before the one containing its own canary, but retains all subsequent values. This means that only one thread can ever “win” and become the manager, since the first thread to send a canary is guaranteed to retain all subsequent canaries, yet also guaranteed its canary will be GC’d. Other threads racing to become the manager will remain blocked until the manager thread is created, since its canaries will be retained by the manager-to-be until it dequeues them.</p><p>(This is the most subtle part of the process to get right, but conveniently, it mostly just works out without very much code. If you didn’t understand any of the above three paragraphs, it isn’t a big deal.)</p></li></ol><p>The final piece to this puzzle is to define the <code>define/cross-phase</code> macro that wraps <code>make-cross-phase</code>. The macro is actually slightly more involved than just generating a call to <code>make-cross-phase</code> directly, since we’d like to use an uninterned symbol for the topic instead of an interned one, just to ensure it is unique. Ordinarily, this might seem impossible, since an uninterned symbol is fundamentally a unique value that needs to be communicated across phases, and the whole problem we are solving is creating a communication channel that spans phases. However, Racket actually provides some built-in support for sharing uninterned symbols across phases (plus some other kinds of values, but they must always be immutable). To do this, we need to generate a <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._cross-phase._persistent-modules%29">cross-phase persistent submodule</a> that exports an uninterned symbol, then pass that symbol as the topic to <code>make-cross-phase</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">define/cross-phase</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">cross-phase-topic-key</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">#%kernel</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%declare</span><span class="w"> </span><span class="kd">#:cross-phase-persistent</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%provide</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">topic</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="s2">"cross-phase"</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">topic-mod-name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">e</span><span class="p">)))))</span></code></pre><p>And that’s really it. We’re done.</p><h3><a name="executing-the-exploit"></a>Executing the exploit</h3><p>With our implementation of <code>define/cross-phase</code> in hand, all that’s left to do is run our original <code>check-foods.rkt</code> program and see what happens:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +set-add!:<span class="w"> </span>contract<span class="w"> </span>violation: +expected:<span class="w"> </span>set? +given:<span class="w"> </span><span class="o">(</span>mutable-set<span class="w"> </span><span class="s2">"fried chicken"</span><span class="w"> </span><span class="s2">"roasted chicken"</span><span class="w"> </span><span class="s2">"roasted potato"</span><span class="w"> </span><span class="s2">"fried potato"</span><span class="o">)</span> +argument<span class="w"> </span>position:<span class="w"> </span>1st +other<span class="w"> </span>arguments...: +<span class="w"> </span>x:<span class="w"> </span><span class="s2">"pineapple"</span></code></pre><p>Well, I don’t know what you expected. Play stupid games, win stupid prizes.</p><p>This error actually makes sense, but it belies one reason (of many) why this whole endeavor is probably a bad idea. Although we’ve managed to make our mutable set cross-phase persistent, our references to set operations like <code>set-add!</code> and <code>set-member?</code> are not, and every time <code>racket/set</code> is instantiated in a fresh phase, it creates an entirely new instance of the <code>set</code> structure type. This means that even though we have a bona fide mutable set, it isn’t actually the type of set that this phase’s <code>set-add!</code> understands!</p><p>Of course, this isn’t a problem that some liberal application of <code>define/cross-phase</code> can’t solve:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-member?</span><span class="w"> </span><span class="nb">set-member?</span><span class="p">)</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-add!</span><span class="w"> </span><span class="nb">set-add!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And thus we find that another so-called “guarantee” isn’t.</p><h2><a name="reflection"></a>Reflection</h2><p>Now comes the time in the blog post when I have to step back and think about what I’ve done. Have mercy.</p><p>Everything in this blog post is a terrible idea. No, you should not use loggers for anything that isn’t logging, you shouldn’t use wills and executors for critical control flow, and obviously you should absolutely not intentionally break one of the most helpful guarantees the Racket module system affords you.</p><p>But I thought it was fun to do all that, anyway.</p><p>The meaningful takeaways from this blog post aren’t that the separate compilation guarantee can be broken, nor that any of the particular techniques I used hold, but that</p><ol><li><p>ensuring non-trivial guarantees is really hard,</p></li><li><p>despite that, the separate compilation guarantee is really, really hard to break,</p></li><li><p>the separate compilation guarantee is good, and you should appreciate the luxury it affords you while writing Racket macros,</p></li><li><p>avoiding races in a concurrent environment can be extremely subtle,</p></li><li><p>and Racket is totally <em>awesome</em> for giving me this much rope to hang myself with.</p></li></ol><p>If you want to hang yourself with Racket, too, <a href="https://gist.github.com/lexi-lambda/f173a84fc9727977bcea657b3bb0cd4f">runnable code from this blog post is available here</a>.</p><ol class="footnotes"><li id="footnote-1"><p>This isn’t <em>strictly</em> true, since Racket provides sandboxing mechanisms that can compile and execute untrusted code without file system or network access, but this is not the default compilation mode. Usually, it doesn’t matter nearly as much as it might sound: most of the time, if you’re compiling untrusted code, you’re also going to run it, and running untrusted code can do all those things, anyway. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>This is actually a <em>terrible</em> use case for a macro, since an ordinary function would do just fine, but I’m simplifying a little to keep the example small. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Racket actually provides this functionality directly via the <code>log-level?</code> procedure. However, since <code>log-level?</code> provides no way to determine how <em>many</em> receivers are listening to a topic, using it to guard against creating a receiver is vulnerable to a race condition that the garbage collection-based approach can avoid, as is discussed later. Furthermore, the GC technique is more likely to be resilient to nosy log receivers listening on all topics at the <code>'debug</code> level, since they will almost certainly dequeue and discard the value quickly (as otherwise they would leak large quantities of memory). <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>This race is the one that makes using <code>log-level?</code> untenable, since the receiver needs to be created before the topic is checked for listeners to avoid the race, which can’t be done with <code>log-level?</code> (since it would always return <code>#t</code>). <a href="#footnote-ref-4-1">↩</a></p></li></ol></article>Macroexpand anywhere with local-apply-transformer!https://lexi-lambda.github.io/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/https://lexi-lambda.github.io/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/06 Oct 2018<article><p>Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only <em>certain positions</em> in Racket code are subject to macroexpansion.</p><p>To illustrate, consider a macro that provides a Clojure-style <code>let</code> form:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This can be used anywhere an expression is expected, and it does as one would expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>However, a novice macro programmer might realize that <code>clj-let</code> really only modifies the syntax of <em>binding pairs</em> for a <code>let</code> form. Therefore, could one define a macro that only adjusts the binding pairs of some existing <code>let</code> form instead of expanding to an entire <code>let</code>? That is, could one write the above example like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>The answer is <em>no</em>: the binding pairs of a <code>let</code> form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion <em>anywhere</em> in a Racket program.</p><h2><a name="why-only-some-positions-are-subject-to-macroexpansion"></a>Why only some positions are subject to macroexpansion</h2><p>To understand <em>why</em> the macroexpander refuses to touch certain positions in a program, we must first understand how the macro system operates. In Racket, a macro is defined as a compile-time function associated with a particular binding, and macros are given complete control over the syntax trees they are surrounded with. If we define a macro <em><code>mac</code></em>, then we write the expression <code>(<em>mac</em> <em>form</em>)</code>, <em><code>form</code></em> is provided as-is to <em><code>mac</code></em> as a syntax object. Its structure can be anything at all, since <em><code>mac</code></em> can be an arbitrary Racket function, and that function can use <em><code>form</code></em> however it pleases.</p><p>To give a concrete illustration, consider a macro that binds some identifiers to symbols in a local scope:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">x</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="ss">hello</span><span class="w"> </span><span class="ss">goodbye</span><span class="p">)</span></code></pre><p>It isn’t the most exciting macro in the world, but it illustrates a key point: the first subform to <code>let-symbols</code> is a list of identifiers that are eventually put in <em>binding</em> position. This means that <code>hello</code> and <code>goodbye</code> are bindings, not uses, and such bindings shadow any existing bindings that might have been in scope:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">foo</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> +<span class="w"> </span><span class="n">foo</span><span class="p">))</span> +<span class="o">&#39;</span><span class="ss">foo</span></code></pre><p>This might not seem very interesting, but it’s critical to understand, since it means that the expander <em>can’t know</em> which sub-pieces of a use of <code>let-symbols</code> will eventually be expressions themselves until it expands the macro and discovers it produces a <code>let</code> form, so it can’t know where it’s safe to perform macroexpansion. To make this more explicit, imagine we define a macro under some name, then try and use that name with our <code>let-symbols</code> macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">x:id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="n">hello</span><span class="p">)</span></code></pre><p>What should the above program do? If we treat the first use of <code>hello</code> in the <code>let-symbols</code> form as a macro application, then <code>(hello goodbye)</code> should be transformed into <code>(goodbye)</code>, and the use of <code>hello</code> in the body should be a syntax error. But if the first use of <code>hello</code> was instead intended to be a binder, then it should shadow the <code>hello</code> definition above, and the output of the program should be <code>'hello</code>.</p><p>To avoid the chaos that would ensue if defining a macro could completely break local reasoning about other macros, Racket chooses the second option, and the program produces <code>'hello</code>. The macroexpander has no way of knowing <em>how</em> each macro will inspect its constituent pieces, so it avoids touching anything until the macro expands. After it discovers the <code>let</code> form in the expansion of <code>let-symbols</code>, it can safely determine that the body expressions are, indeed, expressions, and it can recursively expand any macros they contain. To put things another way, a macro’s sub-forms are never expanded before the macro itself is expanded, only after.</p><h2><a name="forcing-sub-form-expansion"></a>Forcing sub-form expansion</h2><p>The above section explains why the expander must operate as it does, but it’s a little bit unsatisfying. What if we write a macro where we <em>want</em> certain sub-forms to be expanded before they are passed to us? Fortunately, the Racket macro system provides an API to handle this use case, too.</p><p>It is true that the Racket macro system never <em>automatically</em> expands sub-forms before outer forms are expanded, but macro transformers can explicitly op-in to recursive expansion via the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a> function. This function effectively yields control back to the expander to expand some arbitrary piece of syntax as an expression, and when it returns, the macro transformer can inspect the expanded expression however it wishes. In theory, this can be used to implement extensible macros that allow macroexpansion in locations other than expression position.</p><p>To give an example of such a macro, consider the Racket <code>match</code> form, which implements an expressive pattern-matcher as a macro. One of the most interesting qualities of Racket’s <code>match</code> macro is that its pattern language is user-extensible, essentially allowing pattern-level macros. For example, a user might find they frequently match against natural numbers, and they wish to be able to write <code>(nat n)</code> as a shorthand for <code>(? exact-nonnegative-integer? n)</code>. Fortunately, this is easy using <code>define-match-expander</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-match-expander</span><span class="w"> </span><span class="n">nat</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">pat</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">?</span><span class="w"> </span><span class="nb">exact-nonnegative-integer?</span><span class="w"> </span><span class="n">pat</span><span class="p">)]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="mi">-5</span><span class="w"> </span><span class="mi">-2</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">-7</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">list</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">nat</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">n</span><span class="p">])</span> +<span class="mi">4</span></code></pre><p>Clearly, <code>match</code> is somehow expanding the <code>nat</code> match expander as a part of its expansion. Is it using <code>local-expand</code>?</p><p>Well, no. While <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">a previous blog post of mine</a> has illustrated that it is possible to do such a thing with <code>local-expand</code> via some clever trickery, <code>local-expand</code> is really designed to expand <em>expressions</em>. This is a problem, since <code>(nat n)</code> is not an expression, it’s a pattern: it will expand into <code>(? exact-nonnegative-integer? n)</code>, which will lead to a syntax error, since <code>?</code> is not bound in the world of expressions. Instead, for a long while, <code>match</code> and forms like it have emulated how the expander performs macroexpansion in ad-hoc ways. Fortunately, as of Racket v7.0, the new <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Fapply-transformer..rkt%29._local-apply-transformer%29%29"><code>local-apply-transformer</code></a> API provides a way to invoke recursive macroexpansion in a consistent way, and it doesn’t assume that what’s being expanded is an expression.</p><h3><a name="a-closer-look-at-local-apply-transformer"></a>A closer look at <code>local-apply-transformer</code></h3><p>If <code>local-apply-transformer</code> is the answer, what does it actually do? Well, <code>local-apply-transformer</code> allows explicitly invoking a transformer function on some piece of syntax and retrieving the result. In other words, <code>local-apply-transformer</code> allows expanding an arbitrary macro, but since it doesn’t make any assumptions about what the output will be, it only expands it <em>once</em>: just a single step of macro transformation.</p><p>To illustrate, we can write a macro that uses <code>local-apply-transformer</code> to invoke a transformer function and preserve the result using <code>quote-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/apply-transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define-for-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>When we use <code>mac</code>, our <code>flip</code> function will be applied, as a macro, to the syntax object we provide:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="nb">&gt;</span></code></pre><p>Alright, so this works, but it raises some questions. Why is <code>flip</code> defined as a function at phase 1 (using <code>define-for-syntax</code>) instead of as a macro (using <code>define-syntax</code>)? What’s the deal with the <code>'expression</code> argument to <code>local-apply-transformer</code> given that <code>local-apply-transformer</code> is supposedly decoupled from expression expansion? And finally, how is this any different from just calling our <code>flip</code> function on the syntax object directly by writing <code>(flip #'(([x 1]) let x))</code>?</p><p>Let’s start with the first of those questions: why is <code>flip</code> defined as a function rather than as a macro? Well, <code>local-apply-transformer</code> is a fairly low-level operation: remember, it doesn’t assume <em>anything</em> about the argument it’s given! Therefore, it doesn’t take an expression containing a macro and expand it based on its structure, it needs to be explicitly provided the macro transformer function to apply. In practice, this might not seem very useful, since presumably we want to write our macros as macros, not as phase 1 functions. Fortunately, it’s possible to look up the function associated with a macro binding using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, so if we use that, we can define <code>flip</code> using <code>define-syntax</code> as usual:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>Now for the next question: what is the meaning of the <code>'expression</code> argument? This one is more of a historical artifact than anything else: when the expander applies a macro transformer, it does it in a “context”, which is accessible using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-context%29%29"><code>syntax-local-context</code></a> function. This context can be one of a predefined enumeration of cases, including <code>'expression</code>, <code>'top-level</code>, <code>'module</code>, <code>'module-begin</code>, or a list representing a definition context. Whether or not any of those actually apply to our use case, we still have to pick one, but aside from how they affect the value returned by <code>syntax-local-context</code> (which some macros inspect), the value we choose is largely irrelevant. Using <code>'expression</code> will do, even if it’s a bit of a lie.</p><p>Finally, how does any of this differ from just applying the function we get directly? Well, the critical answer is all about <em>hygiene</em>. Racket’s macro system is hygienic, which, among other things, ensures bindings defined with the same name in different places do not unintentionally conflict. Racket’s hygiene mechanism is implemented in the macroexpander, when macro transformers are applied. If we just applied the <code>flip</code> transformer procedure to a syntax object directly, we would circumvent this hygiene mechanism, potentially causing all sorts of problems. By using <code>local-apply-transformer</code>, we ensure hygiene is preserved.</p><p>There is one small problem left with our program, however. Can you spot it? The key is to consider what would happen if we used <code>flip</code> as an ordinary macro, without using <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="n">let:</span><span class="w"> </span><span class="n">bad</span><span class="w"> </span><span class="k">syntax</span> +<span class="w"> </span><span class="n">in:</span><span class="w"> </span><span class="k">let</span></code></pre><p>What happened? Well, remember that when a macro in Racket is used, it receives the whole use site as a syntax object: in this case, <code>#'(flip (([x 1]) let x))</code>. This means that <code>flip</code> ought to be written to parse its argument slightly differently:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span></code></pre><p>Indeed, now that we’ve properly restructured the macro, we can easily switch to using the convenient <code>define-simple-macro</code> shorthand:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This means we also need to update our definition of <code>mac</code> to provide the full syntax object the expander would:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>This might seem redundant, but remember, <code>local-apply-transformer</code> is very low-level! While the convention that <code>(<em>mac</em> . _)</code> is the syntax for a macro transformation might seem obvious, <code>local-apply-transformer</code> makes no assumptions. It just does what we tell it to do.</p><h3><a name="applying-local-apply-transformer"></a>Applying <code>local-apply-transformer</code></h3><p>So what does <code>local-apply-transformer</code> have to do with the problem at the beginning of this blog post? Well, as it happens, we can use <code>local-apply-transformer</code> to implement a macro that allows expansion <em>anywhere</em> using some simple tricks. While it’s true that we cannot magically divine which locations ought to be expanded, what we <em>can</em> do is explicitly annotate which places to expand.</p><p>To do this, we will implement a macro, <code>expand-inside</code>, that looks for subforms annotated with a special <code>$expand</code> identifier and performs macro transformation on those locations before proceeding with ordinary macroexpansion. Using the <code>clj-binding-pairs</code> example from the beginning of this blog post, our solution to that problem will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Put another way, <code>expand-inside</code> will force eager expansion on any subform surrounded with an <code>$expand</code> annotation.</p><p>We’ll start by defining the <code>$expand</code> binding itself. This binding won’t mean anything at all outside of <code>expand-inside</code>, but we’d like it to be a unique binding so that users can rename it (using, <code>rename-in</code>, for example) if they wish. To do this, we’ll use the usual trick of defining it as a macro that always produces an error if it’s ever used:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"illegal outside an ‘expand-inside’ form"</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span></code></pre><p>Next, we’ll implement a syntax class that will form the bulk of our implementation of <code>expand-inside</code>. Since we need to find uses of <code>$expand</code> that might be deeply-nested inside the syntax object provided to <code>expand-inside</code>, we need to recursively look through the syntax object, find any instances of <code>$expand</code>, and put it all back together once we’re done. This can be done relatively cleanly using a recursive syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">do-expand-inside</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="n">$expand</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">$expand</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:do-expand-inside</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">a:do-expand-inside</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">b:do-expand-inside</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">reassembled</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">a.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">reassembled</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">reassembled</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>There are some tricky details to get right in the reassembly of pairs, since syntax lists are actually composed of ordinary pairs rather than syntax pairs, but ultimately, the code for walking a syntax object is small. The key case of this syntax class is the call to <code>do-$expand</code> in the first clause, which we have not yet defined. This function will actually handle performing the expansion by invoking <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">trans</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}})</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">static</span><span class="w"> </span><span class="p">(</span><span class="nb">disjoin</span><span class="w"> </span><span class="nb">procedure?</span><span class="w"> </span><span class="nb">set!-transformer?</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"syntax transformer"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">trans.value</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)])))</span></code></pre><p>This uses the handy <code>static</code> syntax class that comes with <code>syntax/parse</code>, which implicitly handles the call to <code>syntax-local-value</code> and produces a nice error message if the value returned does not match a predicate. All we have to do is apply the transformer value bound to the <code>trans.value</code> attribute using <code>local-apply-transformer</code>, and now the <code>expand-macro</code> can be written in just a couple lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">expand-inside</span> +<span class="w"> </span><span class="kd">#:track-literals</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:do-expand-inside</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form.expansion</span><span class="p">])</span></code></pre><p>(Using the <code>#:track-literals</code> option, also new in Racket v7.0, ensures that Check Syntax will be able to recognize the uses of <code>$expand</code> that disappear from after <code>expand-inside</code> is expanded.)</p><p>Putting everything together, our example from above really works:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>That’s it. All told, the entire implementation is only about 30 lines of code. For a full, compilable, working example, see <a href="https://gist.github.com/lexi-lambda/65d69043023b519694f50dfca2dc7d33">this gist</a>.</p><ol class="footnotes"></ol></article>Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitionshttps://lexi-lambda.github.io/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/https://lexi-lambda.github.io/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/13 Sep 2018<article><p>In my <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">previous blog post</a>, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.</p><h2><a name="what-are-internal-definitions"></a>What are internal definitions?</h2><p>This blog post is going to be largely focused on how to properly implement a form that handles the expansion of <em>internal definitions</em> in Racket. This is a tricky topic to get right, but before we can discuss internal definitions, we have to establish what definitions themselves are and how they relate to other binding forms.</p><p>In a traditional Lisp, there are two kinds of bindings: top-level bindings and local bindings. In Scheme and its descendants, this distinction is characterized by two different binding forms, <code>define</code> and <code>let</code>. To a first approximation, <code>define</code> is used for defining top-level, global bindings, and it resembles variable definitions in many mainstream languages in the sense that definitions using <code>define</code> are not really expressions. They don’t produce a value, they define a new binding. Definitions written with <code>define</code> look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="s2">"hello"</span><span class="p">)</span></code></pre><p>Each definition is made up of two parts: the <em>binding identifier</em>, in this case <code>x</code> and <code>y</code>, and the <em>right hand side</em>, or RHS for short. Each RHS is a single expression that will be evaluated and used as the value for the introduced binding.</p><p>In Scheme and Racket, <code>define</code> also supports a shorthand form for defining functions in a natural syntax without the explicit need to write <code>lambda</code>, which looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">double</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span></code></pre><p>However, this is just syntactic sugar. The above form is really just a macro for the following equivalent, expanded version:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">double</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)))</span></code></pre><p>Since we only care about fully-expanded programs, we’ll focus exclusively on the expanded version of <code>define</code> in this blog post, since if we handle that, we’ll also handle the function shorthand’s expansion.</p><p>In contrast to <code>define</code>, there is also <code>let</code>, which has a rather different shape. A <code>let</code> form <em>is</em> an expression, and it creates local bindings in a delimited scope:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>The binding clauses of a <code>let</code> expression are known as the <em>binding pairs</em>, and the sequence of expressions afterwards are known as the <em>body</em> of the <code>let</code>. Each binding pair consists of a binding identifier and a RHS, just like a top-level definition created with <code>define</code>, but while <code>define</code> is a standalone form, the binding pairs cannot meaningfully exist outside of a <code>let</code>—they are recognized as part of the grammar of the <code>let</code> form itself.</p><p>Like other Lisps, Racket distinguishes between top-level—or, more precisely, <em>module-level</em>—bindings and local bindings. A module-level binding can be exported using <code>provide</code>, which will allow other modules to access the binding by importing the module with <code>require</code>. Such definitions are treated specially by the macroexpander, compiler, and runtime system alike. There is a pervasive, meaningful difference between module-level definitions and local definitions besides simply scope.</p><p>I am making an effort to make this as clear as possible before discussing internal definitions because without it, the following point can be rather confusing: internal definitions are written using <code>define</code>, but they are local bindings, <em>not</em> module-level ones! In Racket, <code>define</code> is allowed to appear in the body of virtually all block forms like <code>let</code>, so the following is a legal program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>This program is equivalent to the one expressed using <code>let</code>. In fact, when the Racket macroexpander expands these local uses of <code>define</code>, it actually translates them into uses of <code>letrec</code>. After expanding the above expression, it would look closer to the following:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span></code></pre><p>In this sense, <code>define</code> is a form with a double life in Racket. When used at the module level, it creates module-level definitions, which remain in a fully-expanded program and can be imported by other modules. When used inside local blocks, it creates internal definitions, which do not remain in fully expanded programs, since they are translated into recursive local binding forms.</p><p>In this blog post, we will ignore module-level definitions. Like in the previous blog post, we will focus exclusively on expanding expressions, not whole modules. However, we will extend our language to allow internal definitions inside local binding forms, and we will translate them into <code>letrec</code> forms in the same way as the Racket macroexpander.</p><h2><a name="revisiting-and-generalizing-the-expression-expander"></a>Revisiting and generalizing the expression expander</h2><p>In the previous blog post, our expander expanded types, which were essentially expressions from the perspective of the Racket macroexpander. We wrote a syntax class that handled the parsing of a restricted type grammar that disallowed most Racket-level expression forms, like <code>begin</code>, <code>if</code>, <code>#%plain-lambda</code>, and <code>quote</code>. After all, Hackett is not dependently-typed, and it disallows explicit type abstraction to preserve type inference, so it would be a very bad thing if we allowed <code>if</code> or explicit lambda abstraction to appear in our types. For this blog post, however, we will restructure the type expander to handle the full grammar of expressions permitted by Racket.</p><p>While the syntax class approach used in the previous blog post was cute, this blog post will use ordinary functions defined at phase 1 instead of syntax classes. In practice, this provides superior error reporting, since it reports syntax errors in terms of the form that went wrong, not the form prior to expansion. Since we can still use <code>syntax-parse</code> to parse the arguments to these functions, we don’t lose any expressive power in the expression of our pattern language.</p><p>To start, we’ll extract the call to <code>local-expand</code> into its own function. This corresponds to the <code>type</code> syntax class from the previous blog post, but we’ll use phase 1 parameters to avoid threading so many explicit function arguments around:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-context</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-stop-list</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">))))</span></code></pre><p>Due to the way <code>local-expand</code> implicitly extends the stop list, as discussed in the previous blog post, we can initialize the stop list to a list containing just <code>define-values</code> and <code>define-syntaxes</code>, and the other forms we care about will be included automatically.</p><p>Next, we’ll use this function to implement a <code>expand-expression</code> function, which will emulate the way the expander expands a single expression, as the name implies. We’ll ignore any custom core forms for now, so we’ll just focus exclusively on the Racket core forms.</p><p>A few of Racket’s core forms are not actually subject to any expansion at all, and they expand to themselves. These forms are <code>quote</code>, <code>quote-syntax</code>, and <code>#%variable-reference</code>. Additionally, <code>#%top</code> is not something useful to handle ourselves, since it involves no recursive expansion, so we’ll treat it as if it expands to itself as well and allow the expander to raise any unbound identifier errors it produces. Here’s what the <code>expand-expression</code> function looks like when exclusively handling these things:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Another set of Racket core forms are simple expressions which contain subforms, all of which are themselves expressions. These forms include things like <code>#%expression</code>, <code>begin</code>, and <code>if</code>, and they can be expanded recursively. We’ll add another clause to handle these, which can be written with a straightforward recursive call to <code>expand-expression</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Another easy form to handle is <code>set!</code>, since it also requires simple recursive expansion, but it can’t be handled in the same way as the above forms since one of its subforms (the variable to mutate) should not be expanded. It needs another small clause:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:set!</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)))]</span></code></pre><p>The other expressions are harder, since they’re all the binding forms. Fully-expanded Racket code has four local binding forms: <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, and <code>letrec-values</code>. Additionally, as discussed in the previous blog post, <code>local-expand</code> can also produce <code>letrec-syntaxes+values</code> forms produced by local syntax bindings. In the type expander, we completely disallowed runtime bindings from appearing in the resulting program, so we completely removed <code>letrec-syntaxes+values</code> in our expansion, but in the case of handling arbitrary Racket programs, we actually want to leave a <code>letrec-values</code> form behind to hold any runtime bindings (i.e. the <code>values</code> part of <code>letrec-syntaxes+values</code>).</p><p>We’ll start with <code>#%plain-lambda</code>, which is the simplest of all the five aforementioned binding forms. It binds a sequence of identifiers at runtime, and they are in scope within the body of the lambda expression. Just as we created and used an internal-definition context to hold the bindings of a <code>letrec-syntax+values</code> form in the previous blog post, we’ll do the same for Racket’s other binding forms as well:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>However, the above handling of <code>#%plain-lambda</code> isn’t <em>quite</em> right, since the argument list can also include a “rest argument” binding in addition to a sequence of positional arguments. To accommodate this, we can introduce a simple syntax class that handles the different permutations:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">plain-formals</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"formals"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[[</span><span class="n">id</span><span class="w"> </span><span class="mi">1</span><span class="p">]]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id*:id</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id**:id</span><span class="p">)</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">id*</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">id**</span><span class="p">]]))</span></code></pre><p>Now we can use this to adjust <code>#%plain-lambda</code> to handle rest arguments:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Next, we’ll handle <code>case-lambda</code>. As it turns out, expanding <code>case-lambda</code> is almost exactly the same as expanding <code>#%plain-lambda</code>, except that it has multiple clauses. Since each clause is expanded identically to the body of a <code>#%plain-lambda</code>, and it even has the same shape, the clauses can be extracted into a separate syntax class to share code between the two forms:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]]))</span></code></pre><p>Now, both <code>#%plain-lambda</code> and <code>case-lambda</code> can be handled in a few lines of code each:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Finally, we need to tackle the three <code>let</code> forms. None of these involve any fundamentally new ideas, but they are a little bit more involved than the variants of lambda due to the need to handle the RHSs. Each variant is slightly different, but not dramatically so: the bindings aren’t in scope when expanding the RHSs of <code>let-values</code>, but they are for <code>letrec-values</code> and <code>letrec-syntaxes+values</code>, and <code>letrec-syntaxes+values</code> creates transformer bindings and must evaluate some RHSs in phase 1 while <code>let-values</code> and <code>letrec-values</code> exclusively bind runtime bindings. It would be possible to implement these three forms in separate clauses, but since we’d ideally like to duplicate as little code as possible, we can write a rather elaborate <code>syntax/parse</code> pattern to handle all three binding forms all at once.</p><p>We’ll start by handling <code>let-values</code> alone to keep things simple:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>This isn’t dramatically different from the implementation of <code>#%plain-lambda</code>. The only difference is that we have to recursively invoke <code>expand-expression</code> on the RHSs in addition to expanding the body expressions. To handle <code>letrec-values</code> in the same clause, however, we’ll have to get a little more creative.</p><p>So far, we haven’t actually tapped very far into <code>syntax/parse</code>’s pattern language over the course of these two blog posts. The full language available to patterns is rather extensive, and we can take advantage of that to write a modification of the above clause that handles both <code>let-values</code> and <code>letrec-values</code> at once:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}}}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>The <code>~bind</code> pattern allows us to explicitly control how attributes are bound as part of the pattern-matching process, which allows us to track when we want to enable the recursive binding behavior of <code>letrec-values</code> in our handler code. Since the vast majority of the logic is otherwise identical, this is a significant improvement over duplicating the clause.</p><p>Adding support for <code>letrec-syntaxes+values</code> is done in the same general way, but the pattern is even more involved. In addition to tracking whether or not the bindings are recursive, we have to track if any syntax bindings were present at all, and if they were, bind them with <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span></code></pre><p>This behemoth clause handles all three varieties of <code>let</code> forms that can appear in the result of <code>local-expand</code>. Notably, in the <code>letrec-syntaxes+values</code> case, we expand into <code>letrec-values</code>, since the transformer bindings are effectively erased, and we use <code>syntax-track-origin</code> to record that the result originally came from a use of <code>letrec-syntaxes+values</code>.</p><p>With these five clauses, we’ve handled all the special forms that can appear in expression position in Racket’s kernel language. To tie things off, we just need to handle the cases of a variable reference, which is represented by a bare identifier not bound to syntax, or literal data, like numbers or strings. We can add one more clause at the end to handle those:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span></code></pre><p>Putting them all together, our <code>expand-expression</code> function looks as follows:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> + +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">])))</span></code></pre><p>If we try it out, we’ll see that it really does work! Even complicated local binding forms are handled properly by our expander:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">))))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">(((</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>We are now able to expand arbitrary Racket expressions in the same way that the expander does. While this might not seem immediately useful—after all, we haven’t actually gained anything here over just calling <code>local-expand</code> with an empty stop list—we can use this as the basis of an expander that can extensibly handle custom core forms, which I may cover in a future blog post.</p><h2><a name="adding-support-for-internal-definitions"></a>Adding support for internal definitions</h2><p>In the previous section, we defined an expander that could expand arbitrary Racket expressions, but our expander is still imperfect: we still do not support internal definitions. For all forms that have bodies, including <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, and <code>letrec-syntaxes+values</code>, Racket permits the use of internal definitions.</p><p>In practice, internal-definition contexts allow for an increased degree of modularity compared to traditional local binding forms, since they provide an <em>extensible</em> binding language. Users may mix many different binding forms within a single definition context, such as <code>define</code>, <code>define-syntax</code>, <code>match-define</code>, and even <code>struct</code>. However, this means the rewriting process described earlier in this blog post is not as simple as detecting the definitions and lifting them into a local binding form, since it’s not immediately apparent which forms are binding forms and which are expressions!</p><p>For this reason, expanding internal-definition contexts happens to be a nontrivial problem in itself. It involves a little more care than expanding expressions does, since it requires using partial expansion to discover which forms are definitions and which forms are expressions. We must take care to never expand too much, but also to expand enough that we reveal all uses of <code>define-values</code> and <code>define-syntaxes</code> (which all definition forms eventually expand into). We also must handle the splicing behavior of <code>begin</code>, which is necessary to allow single forms to expand into multiple definitions.</p><p>We’ll start by writing an <code>expand-body</code> function, which operates similarly to our previous <code>expand-expression</code> function. Unlike <code>expand-expression</code>, <code>expand-body</code> will accept a <em>list</em> of syntax objects, which represents the sequence of forms that make up the body. Logically, each body will create a first-class definition context with <code>syntax-local-make-definition-context</code> to represent the sequence of definitions:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>The bulk of our <code>expand-body</code> function will be a loop that partially expands body forms, adds definitions to the definition context as it discovers them, and returns the expressions and runtime definitions to be rewritten into binding pairs for a <code>letrec-values</code> form. Additionally, the loop will also track so-called <em>disappeared uses</em> and <em>disappeared bindings</em>, which are attached to the expansion using syntax properties to allow tools like DrRacket to learn about the binding structure of phase 1 definitions that are erased as part of macroexpansion.</p><p>The skeleton of this loop is relatively straightforward to write. We will iterate over the syntax objects that make up the body, expand them, and process the expansion using <code>syntax-parse</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">)))))))</span></code></pre><p>The hard part, of course, is actually handling the potential results of that expansion. We need to handle three forms specially: <code>begin</code>, <code>define-values</code>, and <code>define-syntaxes</code>. All other results of partial expansion will be treated as expressions. We’ll start by handling <code>begin</code>, since it’s the simplest case; we only need to prepend the subforms to the list of body forms to be processed, then continue looping:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>However, as is often the case, this isn’t quite perfect, since the information that these forms came from a surrounding <code>begin</code> is lost, which tools like DrRacket want to know. To solve this problem, the expander adjusts the <code>origin</code> property for all spliced forms, which we can mimic using <code>syntax-track-origin</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This is sufficient for <code>begin</code>, so we can move onto the actual definitions themselves. This actually isn’t too hard, since we just need to add the bindings we discover to the first-class definition context and preserve <code>define-values</code> bindings as binding pairs:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This solution is missing one thing, however, which is the use of <code>syntax-local-identifier-as-binding</code> to any use-site scopes that were added to the binding identifier while expanding the binding form in the definition context. Explaining precisely why this is necessary is outside the scope of this blog post, and is best understood by reading <a href="http://www.cs.utah.edu/plt/scope-sets/pattern-macros.html#%28part._use-site%29">the section on use-site scopes</a> in the paper that describes the theory behind Racket’s current macro system, Bindings as Sets of Scopes. In any case, the impact on our implementation is small:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>Finally, as with <code>begin</code>, we want to track that the binding pairs we generate actually came from a use of <code>define-values</code> (which in turn likely came from a use of some other definition form). Therefore, we’ll add another use of <code>syntax-track-origin</code> to copy and extend the necessary properties:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>That’s it for <code>define-values</code>. All that’s left is to handle <code>define-syntaxes</code>, which is quite similar, but instead of storing the definition in a binding pair, its RHS is immediately evaluated and added to the definition context using <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span></code></pre><p>As the above snippet indicates, this is also where the disappeared uses and disappeared bindings come in. In previous cases, we’ve used <code>syntax-track-origin</code> to indicate that a piece of syntax was the result of expanding a different piece of syntax, but in this case, <code>define-syntaxes</code> doesn’t expand into anything at all; it’s simply removed from the expansion entirely. Therefore, we need to resort to tracking the information in syntax properties on the resulting <code>letrec-values</code> form, so we’ll save them for later.</p><p>Finally, to finish things up, we can add a catchall clause that handles all other forms, which are now guaranteed to be expressions:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This completes our loop that processes definition forms, so all that’s left to do is handle the results. The only significant remaining work is to actually expand the RHSs of the binding pairs we collected and the body expressions, which can be done by calling our own <code>expand-expression</code> function directly:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span></code></pre><p>Finally, we can assemble all the pieces together into a single local binding form with the appropriate syntax properties:</p><pre><code class="pygments"><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))</span></code></pre><p>That’s it. We’ve now written an <code>expand-body</code> function that can process internal definition contexts in the same way that the macroexpander does. Overall, the whole function is just under 45 lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)))))</span></code></pre><p>The next step is to actually use this function. We need to replace certain recursive calls to <code>expand-expression</code> with calls to <code>expand-body</code>, but if we do this naïvely, we’ll have some problems. Currently, when we expand body forms, they’re always immediately inside another definition context (i.e. the bindings introduced by lambda formals or by <code>let</code> binding pairs), but they haven’t actually been expanded in that context yet. When we call <code>expand-body</code>, we create a nested context, which will inherit the bindings, but won’t automatically add the parent context’s scope. Therefore, we need to manually call <code>internal-definition-context-introduce</code> on the body syntax objects before calling <code>expand-body</code>. We can write a small helper function to make this easier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="n">stxs</span><span class="w"> </span><span class="n">ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">ctx</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stxs</span><span class="p">))))))</span></code></pre><p>Now we just need to replace the relevant calls to <code>expand-expression</code> with calls to <code>expand-body/in-ctx</code>, starting with a minor adjustment to our <code>lambda-clause</code> syntax class from earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="p">]]))</span></code></pre><p>The only other change must occur in the handling of the various <code>let</code> forms, which similarly replaces <code>expand-expression</code> with <code>expand-body/in-ctx</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">)))]</span></code></pre><p>With these changes, we’ve now extended our expression expander with the ability to expand internal definitions. We can see this in action on a simple example:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>Just as we’d like, the transformer bindings were expanded and subsequently eliminated, and the runtime binding was collected into a <code>letrec-values</code> form. The outer <code>let-values</code> is left over from the outer <code>let</code>, which is needed only to create an internal-definition context to hold our internal definitions.</p><h2><a name="putting-the-expression-expander-to-work"></a>Putting the expression expander to work</h2><p>So far, we’ve done a lot of work to emulate the behavior of Racket’s macroexpander, and as the above example demonstrates, we’ve been fairly successful in that goal. However, you might be wondering <em>why</em> we did any of this, as replicating the behavior of <code>local-expand</code> is not very useful on its own. As mentioned above, this can be used as the foundation of an expander for custom core forms that extends, rather than replaces, the built-in Racket core forms, It can also be used to “cheat” and expand through the behavior of the <code>local-expand</code> stop list, which implicitly adds the Racket core forms to any non-empty stop list. Hopefully, I’ll have a chance to cover some of these things more deeply in the future, but for now, I’ll just give a small taste of the latter.</p><p>By using the power of our <code>expand-expression</code> function, it’s actually possible to use this kind of expression expander to do genuinely nefarious things, such as hijack the behavior of arbitrary macros! For example, we could do something evil like make <code>for</code> loops run in reverse order by adding <code>for</code> to <code>current-stop-list</code>, then adding an additional special case to <code>expand-expression</code> for <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">for</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="k">for</span><span class="p">]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:for</span><span class="w"> </span><span class="p">([</span><span class="n">x:id</span><span class="w"> </span><span class="n">seq:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="p">(</span><span class="nb">sequence-&gt;list</span><span class="w"> </span><span class="n">seq</span><span class="p">)))]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>Amazingly, due to the fact that we’ve taken complete control of the expansion process, this will rewrite uses of <code>for</code> <em>even if they are introduced by macroexpansion</em>. For example, we could write a small macro that expands into a use of <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="nb">in-range</span><span class="w"> </span><span class="n">n</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">i</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span> +<span class="mi">0</span> +<span class="mi">1</span> +<span class="mi">2</span> +<span class="mi">3</span> +<span class="mi">4</span></code></pre><p>If we write a wrapper macro that applies our evil version of <code>expand-expression</code> to its body, then wrap a use of our <code>print-up-to</code> macro with it, it will execute the loop in reverse order:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:expr</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span><span class="p">)])</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>On its own, this is not that impressive, since we could have just used <code>local-expand</code> on the body directly to achieve this. However, what’s remarkable about <code>hijack-for-loops</code> is that it will work even if the <code>for</code> loop is buried deep inside some arbitrary expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">foo</span> +<span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">))))</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="mi">5</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>Of course, this example is rather contrived—mucking with <code>for</code> loops like this isn’t useful at all, and nobody would really write <code>print-up-to</code> as a macro, anyway—but there is potential for using this technique to do more interesting things.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p>The system outlined in this blog post is not something I would recommend using in any real macro. It is enormously complicated, requires knowledge well above that of your average working macrologist, and it involves doing rather horrible things to the macro system, things it was undoubtably never designed to do. Still, I believe this blog post is useful, for a few different reasons:</p><ol><li><p>The technology outlined in this post, while perhaps not directly applicable to existing real-world problems, provides a framework for implementing various new kinds of syntax transformations in Racket <em>without</em> extending the macro system. It demonstrates the expressive power of the macro system, and it hopefully lays the foundation for a better, more high-level interface for users who wish to define their own languages with custom core forms.</p></li><li><p>This system provides insight into the way the Racket macroexpander operates, <em>in terms of the userspace syntax API</em>. The canonical existing model of hygienic macroexpansion, in the aforementioned <a href="http://www.cs.utah.edu/plt/scope-sets/">Bindings as Sets of Scopes</a> paper, does not explain the workings of internal definition contexts in detail, and it certainly doesn’t explain them in terms that a Racket programmer would already be familiar with. By reencoding those ideas within the macro system itself, an advanced macro writer may be able to more easily connect concepts in the macro system’s implementation to concepts they have already been exposed to.</p></li><li><p>The capability of the proof-of-concept outlined here demonstrates that the limitation imposed by the existing implementation of the stop list (namely, the way it is implicitly extended with additional identifiers) is essentially artificial, and it can be hacked around with sufficient (albeit significant) effort. This isn’t enormously important, but it is somewhat relevant to a recent debate in <a href="https://github.com/racket/racket/issues/2154">a GitHub issue</a> about the handling of the <code>local-expand</code> stop list.</p></li><li><p>Finally, for myself as much as anyone else, this implementation records in a concise way (perhaps overly concise at times) the collection of very subtle details I’ve learned over the past six months about how information is preserved and propagated during the expansion process.</p></li></ol><p>This blog post is not for everybody. If you made it to the end, give yourself a pat on the back. If you made it to the end <em>and</em> understood everything you read: congratulations, you are a certified expert in Racket macro programming. If not, do not fear, and do not lose hope—I plan for something significantly more mellow next time.</p><p>As always, I’d like to give thanks to the people who contributed significantly, if indirectly, to the contents of this blog post, namely <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://mballantyne.net">Michael Ballantyne</a>, and <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a>. And finally, for those interested, all of the code in this blog post can be found in a runnable form <a href="https://gist.github.com/lexi-lambda/c4f4b91ac9c0a555447d72d02e18be7b">in this GitHub gist</a>.</p><ol class="footnotes"></ol></article>Reimplementing Hackett’s type language: expanding to custom core forms in Rackethttps://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/https://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/15 Apr 2018<article><p>In the past couple of weeks, I <a href="https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935">completely rewrote the implementation of Hackett’s type language</a> to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.</p><p>This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">my previous blog post on Hackett</a>, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.</p><h2><a name="what-are-core-forms"></a>What are core forms?</h2><p>Before we can get started writing <em>custom core forms</em>, we need to understand the meaning of Racket’s plain old <em>core forms</em>. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.</p><p>To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a <code>#lang</code> line at the top. In this case, we’ll use <code>#lang racket</code> to keep things simple:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as <em>reading</em> the program. The <code>#lang</code> line controls how the program is read—some <code>#lang</code>s provide parsers that allow syntax that is very different from the parser used for <code>#lang racket</code>—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span></code></pre><p>Note the introduction of <code>#%module-begin</code>. Despite the fancy name, this is really just an ordinary macro provided by the <code>racket</code> language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with <code>#%module-begin</code>; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.</p><p>One the program has been read, it is subsequently <em>expanded</em> by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">2</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">call-with-values</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="n">add2</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">))</span> +<span class="w"> </span><span class="n">print-values</span><span class="p">)))</span></code></pre><p>Let’s note the things that changed:</p><ol><li><p><code>#%module-begin</code> was replaced with <code>#%plain-module-begin</code>. <code>#%plain-module-begin</code> is a binding that wraps the body of every expanded module, and all definitions of <code>#%module-begin</code> in any language must eventually expand to <code>#%plain-module-begin</code>. However, <code>#lang racket</code>’s <code>#%module-begin</code> doesn’t <em>just</em> expand to <code>#%plain-module-begin</code>, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints <code>5</code> even though there is no code related to printing in the original program!</p></li><li><p>The lambda shorthand used with <code>define</code> was converted to an explicit use of <code>lambda</code>, and it was expanded to <code>define-values</code>. In Racket, <code>define</code> and <code>define-syntax</code> are really just macros for <code>define-values</code> and <code>define-syntaxes</code> that only bind a single identifier.</p></li><li><p>All function applications were tagged explicitly with <code>#%plain-app</code>. This syntactically distinguishes function applications from uses of forms like <code>define-values</code> or <code>lambda</code>. It also allows languages to customize function application by providing their own macros named <code>#%app</code> (just like languages can provide their own macros named <code>#%module-begin</code> that expand to <code>#%plain-module-begin</code>), but that is outside the scope of this blog post.</p></li><li><p>All literals have been wrapped with <code>quote</code>, so <code>2</code> became <code>'2</code> and <code>3</code> became <code>'3</code>.</p></li></ol><p>Importantly, the resulting program contains <strong>no macros</strong>. Such programs are called <em>fully expanded</em>, since all macros have been eliminated and no further expansion can take place.</p><p>So what’s left behind? Well, some of the things in the program are literal data, like the numbers <code>2</code> and <code>3</code>. There are also some variable references, <code>x</code> and <code>add2</code>. Most of the program, however, is built out of primitives like <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, and <code>lambda</code>. These primitives are <em>core forms</em>—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.</p><p>In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:</p><pre><code class="pygments"><span class="n">var</span> <span class="n">add2</span> <span class="o">=</span> + <span class="n">function</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span> + +<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">add2</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span></code></pre><p>Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be <em>compiled</em>. The Racket compiler has built-in rules for how to compile core forms like <code>define-values</code>, <code>lambda</code>, and <code>#%plain-app</code>, and the result is optimized Racket bytecode.</p><p>In the remainder of this blog post, as most discussions of macros do, we’ll ignore the <em>read</em> and <em>compile</em> steps of the Racket program pipeline and focus exclusively on the <em>expand</em> step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.</p><h3><a name="racket-s-default-core-forms"></a>Racket’s default core forms</h3><p>So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, <code>lambda</code>, and <code>quote</code>, but there are many more. The full list is available in the section of the Racket reference named <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29">Fully Expanded Programs</a>, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like <code>define-syntaxes</code>, <code>if</code>, <code>let-values</code>, <code>letrec-values</code>, <code>begin</code>, <code>quote-syntax</code>, and <code>set!</code>. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.</p><p>These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, <code>#lang racket</code> implements <code>cond</code> as a macro that eventually expands into <code>if</code>, and it implements <code>syntax</code> as a macro that eventually expands into function calls and <code>quote-syntax</code>. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s <code>match</code> can expand into uses of <code>let</code> and <code>cond</code>, and it doesn’t need to concern itself with using <code>let-values</code> and <code>if</code>. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.</p><h3><a name="the-need-for-custom-core-forms"></a>The need for custom core forms</h3><p>With that in mind, why might we wish to define <em>custom</em> core forms? In fact, what would such a thing even mean? By their very nature, <em>all</em> Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.</p><p>Despite this, there <em>are</em> at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.</p><h4><a name="supporting-multiple-backends"></a>Supporting multiple backends</h4><p>The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket <code>#lang</code> that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might <em>also</em> wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.</p><p>In the case of a hardware description language, the custom core forms might include things like <code>input</code> and <code>output</code> for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could <em>additionally</em> define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.</p><p>Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.</p><h4><a name="compiling-an-extensible-embedded-language"></a>Compiling an extensible embedded language</h4><p>A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.</p><p>This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it <em>will</em> eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.</p><p>Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the <code>match</code> macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.</p><p>Existing DSLs of this type are rare, but they do exist. <code>syntax/parse</code> provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from <code>racket/match</code> to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While <code>match</code> is essentially just a traditional pattern-matcher, albeit an extensible one, <code>syntax-parse</code> is its own programming language, closer in some ways to Prolog than to Racket.</p><p>For this reason, <code>syntax/parse</code> has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as <a href="http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29"><em>pattern directives</em></a>. Here is an example of pattern directives in action, from my own <code>threading</code> library:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>Each directive is represented by a keyword, in this case <code>#:do</code> and <code>#:with</code>. Each directive has a corresponding keyword in the pattern language, in this case <code>~do</code> and <code>~parse</code>. Therefore, the above pattern could equivalently be written this way:</p><pre><code class="pygments"><span class="p">[{</span><span class="n">~and</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">~do</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using <code>#:declare</code>, so this is also equivalent:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">expr</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a <code>#:with</code> or <code>~parse</code> pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using <code>#:do</code> or <code>~do</code>, practical uses of <code>syntax/parse</code> really do involve quite a lot of programming in its pattern DSL.</p><p>But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—<code>#:declare</code>, <code>#:do</code>, and <code>#:with</code>, among others—are essentially the core forms of <code>syntax/parse</code>’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.</p><p>But what if <code>syntax/parse</code> could define its own core forms? What if, instead of <code>#:do</code>, <code>#:declare</code>, and <code>#:with</code> being implemented as keyword options specially recognized by the <code>syntax-parse</code> grammar, it defined <code>do</code>, <code>declare</code>, and <code>with</code> as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the <code>syntax/parse</code> core forms. The implementation of <code>syntax/parse</code> could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.</p><p>Now, to be fair, <code>syntax/parse</code> is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.</p><p>The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.</p><h2><a name="the-need-for-a-custom-type-language-in-hackett"></a>The need for a custom type language in Hackett</h2><p>In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for <em>both</em> of them:</p><ul><li><p>Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.</p></li><li><p>Hackett can <em>also</em> make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.</p></li></ul><p>The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:</p><ul><li><p><code>(#%type:con <em>id</em>)</code> — Type constructors, like <code>Integer</code> or <code>Maybe</code>. These are one of the fundamental building blocks of Hackett types.</p></li><li><p><code>(#%type:app <em>type</em> <em>type</em>)</code> — Type application, such as <code>(Maybe Integer)</code>. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of <code>#%type:app</code>.</p></li><li><p><code>(#%type:forall <em>id</em> <em>type</em>)</code> — Universal quantification. This is essentially a binding form, which binds any uses of <code>(#%type:bound-var <em>id</em>)</code> in <code><em>type</em></code>.</p></li><li><p><code>(#%type:qual <em>type</em> <em>type</em>)</code> — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like <code>Eq</code> are bound as type constructors.</p></li><li><p>Finally, Hackett types support three different varieties of type variables:</p><ul><li><p><code>(#%type:bound-var <em>id</em>)</code> — Bound type variables. These are only legal under a corresponding <code>#%type:forall</code>.</p></li><li><p><code>(#%type:wobbly-var <em>id</em>)</code> — Solver variables, which may unify with any other type as part of the typechecking process.</p></li><li><p><code>(#%type:rigid-var <em>id</em>)</code> — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.</p></li></ul></li></ul><p>To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we <em>can</em> bind them to macros that do nothing but raise an exception if something attempts to expand them:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span></code></pre><p>This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.</p><h3><a name="expanding-types-in-our-type-language"></a>Expanding types in our type language</h3><p>We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we <em>do</em> want to expand some of their subforms. For example, in the type <code>(#%type:app a b)</code>, we want to recursively expand <code>a</code> and <code>b</code>.</p><p>In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a>, and it has an option relevant to our needs: the stop list.</p><p>Often, <code>local-expand</code> is used to force the expander to completely, recursively expand a form. For example, by using <code>local-expand</code>, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="c1">; =&gt; (let-values ([(x) &#39;1]) (#%plain-app + x &#39;2))</span></code></pre><p>The third argument to <code>local-expand</code> is the <em>stop list</em>, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; =&gt; (#%type:forall x t)</span></code></pre><p>Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the <code>#%type:forall</code> identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding <code>t</code> since the expander has no way of knowing which pieces of <code>(#%type:forall x t)</code> it should expand! In this case, we want it to recur to expand <code>t</code>, since it should be a type, but not <code>x</code>, since <code>#%type:forall</code> essentially puts <code>x</code> in binding position.</p><p>Therefore, we have to get more clever. We need to call <code>local-expand</code> to produce a type, then we have to pattern-match on it and subsequently call <code>local-expand</code> <em>again</em> on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.</p><p>One good way to do this is to use <code>syntax/parse</code> syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using <code>local-expand</code> and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled <code>#%type:con</code> and <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:expanded-type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]))</span></code></pre><p>This blog post is definitely <em>not</em> a <code>syntax/parse</code> tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named <code>expansion</code>. This attribute contains the fully expanded version of the type currently being parsed. In the <code>#%type:con</code> case, <code>expansion</code> is just <code>this-syntax</code>, which holds the current piece of syntax being parsed. This makes sense, since uses of <code>#%type:con</code> just expand to themselves—expanding <code>(#%type:con Maybe)</code> should not perform any additional expansion on <code>Maybe</code>. This is one of Hackett’s atomic types.</p><p>In contrast, <code>#%type:app</code> <em>does</em> recursively expand its arguments. By annotating its two subforms with <code>:type</code>, the <code>type</code> syntax class will invoke <code>local-expand</code> on each subform, which will in turn use <code>expanded-type</code> to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once <code>a</code> and <code>b</code> have been expanded, <code>#%type:app</code> reassembles them into a new syntax object using <code>#'(#%type:app a.expansion b.expansion)</code>, which replaces their unexpanded versions with their new, expanded versions.</p><p>We can see this behavior by writing a small <code>expand-type</code> function that will expand its argument:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>Now we can use it to observe what happens when we try expanding a type using <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; #%type:app: expected type</span> +<span class="c1">; at: Maybe</span> +<span class="c1">; in: (#%type:app Maybe Integer)</span></code></pre><p>Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined <code>Maybe</code> or <code>Integer</code> anywhere. Let’s do so! We can define them as simple macros that expand into uses of <code>#%type:con</code>, which can be done easily using <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29"><code>make-variable-like-transformer</code></a> from <code>syntax/transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Maybe</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Integer</span><span class="p">)))</span></code></pre><p>Now, if we try expanding that same type again:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Integer))</span></code></pre><p>…it works! Neat. Now we just need to add the cases for the remaining forms in our type language:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">t:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like <code>let-syntax</code> to work in types. For example, we ought to be able to create a local type binding with <code>let-syntax</code> and have it just work. Unfortunately, it doesn’t:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; let-syntax: expected one of these identifiers: `#%type:con&#39;, `#%type:app&#39;, `#%type:forall&#39;, `#%type:qual&#39;, `#%type:bound-var&#39;, `#%type:wobbly-var&#39;, or `#%type:rigid-var&#39;</span> +<span class="c1">; at: letrec-syntaxes+values</span> +<span class="c1">; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))</span></code></pre><p>What went wrong? And why is it complaining about <code>letrec-syntaxes+values</code>? Well, if you read the documentation for <code>local-expand</code>, you’ll find that its behavior is a little more complicated than you might at first believe:</p><blockquote><p>If <em><code>stop-ids</code></em> is [a nonempty list containing more than just <code>module*</code>], then <code>begin</code>, <code>quote</code>, <code>set!</code>, <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, <code>if</code>, <code>begin0</code>, <code>with-continuation-mark</code>, <code>letrec-syntaxes+values</code>, <code>#%plain-app</code>, <code>#%expression</code>, <code>#%top</code>, and <code>#%variable-reference</code> are implicitly added to <em><code>stop-ids</code></em>. Expansion stops when the expander encounters any of the forms in <em><code>stop-ids</code></em>, and the result is the partially-expanded form.</p></blockquote><p>That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with <code>letrec-syntaxes+values</code> (which <code>let-syntax</code> expands to) is a reasonable one. If the expander naïvely expanded <code>letrec-syntaxes+values</code> in the presence of a nonempty stop list, it could cause some significant problems!</p><p>Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">))</span></code></pre><p>We see <code>let-syntax</code>, so we start by evaluating the expression on the right hand side of the <code>Bool</code> binding. This produces a transformer expression, so we bind <code>Bool</code> to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:</p><pre><code class="pygments"><span class="c1">; local bindings:</span> +<span class="c1">; Bool -&gt; #&lt;variable-like-transformer&gt;</span> +<span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)</span></code></pre><p>Now, the identifier in application position is <code>#%type:app</code>, and <code>#%type:app</code> is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the <code>let-syntax</code> needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is <code>(#%type:app Maybe Bool)</code>. But this is a problem, because when we then go to expand <code>Bool</code>, <code>Bool</code> isn’t in the local binding table anymore! The <code>let-syntax</code> was already erased, and <code>Bool</code> is unbound!</p><p>When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.</p><p>Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call <code>local-expand</code>. The bad news is that first-class definition contexts are <em>complicated</em>, and using them properly is a surprisingly subtle problem.</p><p>Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our <code>type</code> and <code>expanded-type</code> syntax classes so that we may thread a definition context around as we recursively expand:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now, we can add an additional case to <code>expanded-type</code> to handle <code>letrec-syntaxes+values</code>, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>But even this isn’t quite right. The problem with this implementation is that it throws away the existing <code>intdef-ctx</code> argument to <code>expanded-type</code>, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a <em>child</em> of the previous definition context by passing the old context as an argument to <code>syntax-local-make-definition-context</code>. This will ensure the parent bindings are brought into scope when expanding using the child context:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>With this in place, our example using <code>let-syntax</code> actually works!</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Bool))</span></code></pre><p>Pretty cool, isn’t it?</p><h3><a name="preserving-syntax-properties-and-source-locations"></a>Preserving syntax properties and source locations</h3><p>We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.</p><p>To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports <code>#%type:app</code> under a different name, like <code>#%type:apply</code>, we should expand to a piece of syntax that still has <code>#%type:apply</code> in application position instead of replacing it with <code>#%type:app</code>.</p><p>To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the <code>#%type:app</code> clause to the following:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span></code></pre><p>But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy <em>everything</em>, we can define two helper macros, <code>syntax/loc/props</code> and <code>quasisyntax/loc/props</code>, which are like <code>syntax/loc</code> and <code>quasisyntax/loc</code> but copy properties in addition to source location information:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span></code></pre><p>Using <code>syntax/loc/props</code>, we can be truly thorough about ensuring all properties are preserved:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span></code></pre><p>Applying this to the other relevant clauses, we get an updated version of the <code>expanded-type</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now we’re getting closer, but if you can believe it, even <em>this</em> isn’t good enough. The real expander’s implementation of <code>letrec-syntaxes+values</code> does two things our implementation does not: it copies properties and updates the <code>'origin</code> property to indicate the syntax came from a use of <code>letrec-syntaxes+values</code>, and it adds a <code>'disappeared-use</code> property to record the erased bindings for use by tools like DrRacket. We can apply <code>syntax-track-origin</code> and <code>internal-definition-context-track</code> to the resulting syntax to add the same properties the expander would:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span></code></pre><p>Now we’ve <em>finally</em> dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.</p><h3><a name="connecting-our-custom-language-to-hackett"></a>Connecting our custom language to Hackett</h3><p>It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-meta</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/parse</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/intdef</span> +<span class="w"> </span><span class="n">threading</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a <em>typechecker</em>. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.</p><p>But we don’t <em>just</em> need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom <code>#%app</code> binding that converts n-ary type applications to nested uses of <code>#%type:app</code>, as well as a nicer <code>forall</code> macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of <code>#%app</code>—one for types and one for values—since <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">Hackett supports multiple namespaces</a>, and each can have its own <code>#%app</code> binding.</p><p>The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom <code>syntax/parse</code> pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, <a href="https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt">it’s available on GitHub here</a>.</p><h2><a name="evaluation-limitations-and-acknowledgements"></a>Evaluation, limitations, and acknowledgements</h2><p>Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started <a href="https://twitter.com/lexi_lambda/status/976533916596097024">my new job</a> this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net <em>removal</em> of 100 lines from the rest of the codebase, which I consider an organizational win.</p><p>As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I <a href="https://twitter.com/lexi_lambda/status/985051504867446786">able to get it working</a> quickly and easily, I was able to do it in <a href="https://twitter.com/lexi_lambda/status/985052476473856000">no more than 20 lines of code</a>. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.</p><p>Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the <code>local-expand</code> stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes <code>syntax-parameterize</code>, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like <code>syntax-parameterize</code> cooperate better with things like Hackett’s type language.</p><p>Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> for <a href="https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf">his incredible work on constructing tools for the working macro developer</a>, including writing the fantastic <code>syntax/parse</code> library that powers essentially everything I do. Thank you both.</p><ol class="footnotes"></ol></article>An opinionated guide to Haskell in 2018https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/10 Feb 2018<article><p>For me, this month marks the end of an era in my life: as of February 2018, I am no longer employed writing Haskell. It’s been a fascinating two years, and while I am excitedly looking forward to what I’ll be doing next, it’s likely I will continue to write Haskell in my spare time. I’ll probably even write it again professionally in the future.</p><p>In the meantime, in the interest of both sharing with others the small amount of wisdom I’ve gained and preserving it for my future self, I’ve decided to write a long, rather dry overview of a few select parts of the Haskell workflow I developed and the ecosystem I settled into. This guide is, as the title notes, <em>opinionated</em>—it is what I used in my day-to-day work, nothing more—and I don’t claim that anything here is the only way to write Haskell, nor even the best way. It is merely what I found helpful and productive. Take from it as much or as little as you’d like.</p><h2><a name="build-tools-and-how-to-use-them"></a>Build tools and how to use them</h2><p>When it comes to building Haskell, you have options. And frankly, most of them are pretty good. There was a time when <code>cabal-install</code> had a (warranted) reputation for being nearly impossible to use and regularly creating dependency hell, but I don’t think that’s the case anymore (though you <em>do</em> need to be a little careful about how you use it). Sandboxed builds work alright, and <code>cabal new-build</code> and the other <code>cabal new-*</code> commands are even better. That said, the UX of <code>cabal-install</code> is still less-than-stellar, and it has sharp edges, especially for someone coming from an ecosystem without a heavyweight compilation process like JavaScript, Ruby, or Python.</p><p>Nix is an alternative way to manage Haskell dependencies, and it seems pretty cool. It has a reputation for being large and complicated, and that reputation does not seem especially unfair, but you get lots of benefits if you’re willing to pay the cost. Unfortunately, I have never used it (though I’ve read a lot about it), so I can’t comment much on it here. Perhaps I’ll try to go all-in with Nix when I purchase my next computer, but for now, my workflow works well enough that I don’t feel compelled to switch.</p><p>Personally, I use <code>stack</code> as my Haskell build tool. It’s easy to use, it works out of the box, and while it doesn’t enjoy the same amount of caching as <code>cabal new-build</code> or Nix, it caches most packages, and it also makes things like git-hosted sources incredibly easy, which (as far as I can tell) can’t be done with <code>cabal-install</code> alone.</p><p>This section is going to be a guide on how <em>I</em> use <code>stack</code>. If you use <code>cabal-install</code> with or without Nix, great! Those tools seem good, too. This is not an endorsement of <code>stack</code> over the other build tools, just a description of how I use it, the issues I ran into, and my solutions to them.</p><h3><a name="understanding-stack-s-model-and-avoiding-its-biggest-gotcha"></a>Understanding <code>stack</code>’s model and avoiding its biggest gotcha</h3><p>Before using <code>stack</code>, there are a few things every programmer should know:</p><ul><li><p><code>stack</code> is not a package manager, it is a build tool. It does not manage a set of “installed” packages; it simply builds targets and their dependencies.</p></li><li><p>The command to build a target is <code>stack build &lt;target&gt;</code>. Just using <code>stack build</code> on its own will build the current project’s targets.</p></li><li><p><strong>You almost certainly do not want to use <code>stack install</code>.</strong></p></li></ul><p>This is the biggest point of confusion I see among new users of <code>stack</code>. After all, when you want to install a package with <code>npm</code>, you type <code>npm install &lt;package&gt;</code>. So a new Haskeller decides to install <code>lens</code>, types <code>stack install lens</code>, and then later tries <code>stack uninstall lens</code>, only to discover that no such command exists. What happened?</p><p><code>stack install</code> is not like <code>npm install</code>. <code>stack install</code> is like <code>make install</code>. It is nothing more than an alias for <code>stack build --copy-bins</code>, and <em>all</em> it does is build the target and copy all of its executables into some relatively global location like <code>~/.local/bin</code>. This is usually not what you want.</p><p>This design decision is not unique to <code>stack</code>; <code>cabal-install</code> suffers from it as well. One can argue that it isn’t unintuitive because it really is just following what <code>make install</code> conventionally does, and the fact that it happens to conflict with things like <code>npm install</code> or even <code>apt-get install</code> is just a naming clash. I think that argument is a poor one, however, and I think the decision to even include a <code>stack install</code> command was a bad idea.</p><p>So, remember: don’t use <code>stack install</code>! <code>stack</code> works best when everything lives inside the current project’s <em>local</em> sandbox, and <code>stack install</code> copies executables into a <em>global</em> location by design. While it might sometimes appear to work, it’s almost always wrong. The <em>only</em> situation in which <code>stack install</code> is the right answer is when you want to install an executable for a use unrelated to Haskell development (that is, something like <code>pandoc</code>) that just so happens to be provided by a Haskell package. <strong>This means no running <code>stack install ghc-mod</code> or <code>stack install intero</code> either, no matter what READMEs might tell you!</strong> Don’t worry: I’ll cover the proper way to install those things later.</p><h3><a name="actually-building-your-project-with-stack"></a>Actually building your project with <code>stack</code></h3><p>Okay, so now that you know to never use <code>stack install</code>, what <em>do</em> you use? Well, <code>stack build</code> is probably all you need. Let’s cover some variations of <code>stack build</code> that I use most frequently.</p><p>Once you have a <code>stack</code> project, you can build it by simply running <code>stack build</code> within the project directory. However, for local development, this is usually unnecessarily slow because it runs the GHC optimizer. For faster development build times, pass the <code>--fast</code> flag to disable optimizations:</p><pre><code>$ stack build --fast +</code></pre><p>By default, <code>stack</code> builds dependencies with coarse-grained, package-level parallelism, but you can enable more fine-grained, module-level parallel builds by adding <code>--ghc-options=-j</code>. Unfortunately, there are conflicting accounts on whether or not this actually makes things faster or slower in practice, and I haven’t extensively tested to see whether or not this is the case, so I mostly leave it off.</p><p>Usually, you also want to build and run the tests along with your code, which you can enable with the <code>--test</code> flag. Additionally, <code>stack test</code> is an alias for <code>stack build --test</code>, so these two commands are equivalent:</p><pre><code>$ stack build --fast --test +$ stack test --fast +</code></pre><p>Also, it is useful to build documentation as well as code! You can do this by passing the <code>--haddock</code> flag, but unfortunately, I find Haddock sometimes takes an unreasonably long time to run. Therefore, since I usually only care about running Haddock on my dependencies, I usually pass the <code>--haddock-deps</code> flag instead, which prevents having to re-run Haddock every time you build:</p><pre><code>$ stack test --fast --haddock-deps +</code></pre><p>Finally, I usually want to build and test my project in the background whenever my code changes. Fortunately, this can be done easily by using the <code>--file-watch</code> flag, making it easy to incrementally change project code and immediately see results:</p><pre><code>$ stack test --fast --haddock-deps --file-watch +</code></pre><p>This is the command I usually use to develop my Haskell projects.</p><h3><a name="accessing-local-documentation"></a>Accessing local documentation</h3><p>While Haskell does not always excel on the documentation front, a small amount of documentation is almost always better than no documentation at all, and I find my dependencies’ documentation to be an invaluable resource while developing. I find many people just look at docs on Hackage or use the hosted instance of Hoogle, but this sometimes leads people astray: they might end up looking at the wrong version of the documentation! Fortunately, there’s an easy solution to this problem, which is to browse the documentation <code>stack</code> installs locally, which is guaranteed to match the version you are using in your current project.</p><p>The easiest way to open local documentation for a particular package is to use the <code>stack haddock --open</code> command. For example, to open the documentation for <code>lens</code>, you could use the following command:</p><pre><code>$ stack haddock --open lens +</code></pre><p>This will open the local documentation in your web browser, and you can browse it at your leisure. If you have already built the documentation using the <code>--haddock-deps</code> option I recommended in the previous section, this command should complete almost instantly, but if you haven’t built the documentation yet, you’ll have to wait as <code>stack</code> builds it for you on-demand.</p><p>While this is a good start, it isn’t perfect. Ideally, I want to have <em>searchable</em> documentation, and fortunately, this is possible to do by running Hoogle locally. This is easy enough with modern versions of <code>stack</code>, which have built-in Hoogle integration, but it still requires a little bit of per-project setup, since you need to build the Hoogle search index with the following command:</p><pre><code>$ stack hoogle -- generate --local +</code></pre><p>This will install Hoogle into the current project if it isn’t already installed, and it will index your dependencies’ documentation and generate a new Hoogle database. Once you’ve done that, you can start a web server that serves a local Hoogle search page with the following command:</p><pre><code>$ stack hoogle -- server --local --port=8080 +</code></pre><p>Navigate to <code>http://localhost:8080</code> in your web browser, and you’ll have a fully-searchable index of all your Haskell packages’ documentation. Isn’t that neat?</p><p>Unfortunately, you <em>will</em> have to manually regenerate the Hoogle database when you install new packages and their documentation, which you can do by re-running <code>stack hoogle -- generate --local</code>. Fortunately, regenerating the database doesn’t take very long, as long as you’ve been properly rebuilding the documentation with <code>--haddock-deps</code>.</p><h3><a name="configuring-your-project"></a>Configuring your project</h3><p>Every project built with <code>stack</code> is configured with two separate files:</p><ul><li><p>The <code>stack.yaml</code> file, which controls which packages are built and what versions to pin your dependencies to.</p></li><li><p>The <code>&lt;project&gt;.cabal</code> file <em>or</em> <code>package.yaml</code> file, which specifies build targets, their dependencies, and which GHC options to apply, among other things.</p></li></ul><p>The <code>.cabal</code> file is, ultimately, what is used to build your project, but modern versions of <code>stack</code> generate projects that use hpack, which uses an alternate configuration file, the <code>package.yaml</code> file, to generate the <code>.cabal</code> file. This can get a little bit confusing, since it means you have <em>three</em> configuration files in your project, one of which is generated from the other one.</p><p>I happen to use and like hpack, so I use a <code>package.yaml</code> file and allow hpack to generate the <code>.cabal</code> file. I have no real love for YAML, and in fact I think custom configuration formats are completely fine, but the primary advantage of hpack is the ability to specify things like GHC options and default language extensions for all targets at once, instead of needing to duplicate them per-target.</p><p>You can think of the <code>.cabal</code> or <code>package.yaml</code> file as a specification for <em>how</em> your project is built and <em>what packages</em> it depends on, but the <code>stack.yaml</code> file is a specification of precisely <em>which version</em> of each package should be used and where it should be fetched from. Also, each <code>.cabal</code> file corresponds to precisely <em>one</em> Haskell package (though it may have any number of executable targets), but a <code>stack.yaml</code> file can specify multiple different packages to build, useful for multi-project builds that share a common library. The details here can be a little confusing, more than I am likely going to be able to explain in this blog post, but for the most part, you can get away with the defaults unless you’re doing something fancy.</p><h3><a name="setting-up-editor-integration"></a>Setting up editor integration</h3><p>Currently, I use Atom to write Haskell. Atom is not a perfect editor by any means, and it leaves a lot to be desired, but it’s easy to set up, and the Haskell editor integration is decent.</p><p>Atom’s editor integration is powered by <code>ghc-mod</code>, a program that uses the GHC API to provide tools to inspect Haskell programs. Installing <code>ghc-mod</code> must be done manually so that Atom’s <code>haskell-ghc-mod</code> package can find it, and this is where a lot of people get tripped up. They run <code>stack install ghc-mod</code>, it installs <code>ghc-mod</code> into <code>~/.local/bin</code>, they put that in their <code>PATH</code>, and things work! …except when a new version of GHC is released a few months later, everything stops working.</p><p>As mentioned above, <strong><code>stack install</code> is not what you want</strong>. Tools like <code>ghc-mod</code>, <code>hlint</code>, <code>hoogle</code>, <code>weeder</code>, and <code>intero</code> work best when installed as part of the sandbox, <em>not</em> globally, since that ensures they will match the current GHC version your project is using. This can be done per-project using the ordinary <code>stack build</code> command, so the easiest way to properly install <code>ghc-mod</code> into a <code>stack</code> project is with the following command:</p><pre><code>$ stack build ghc-mod +</code></pre><p>Unfortunately, this means you will need to run that command inside every single <code>stack</code> project individually in order to properly set it up so that <code>stack exec -- ghc-mod</code> will find the correct executable. One way to circumvent this is by using a recently-added <code>stack</code> flag designed for this explicit purpose, <code>--copy-compiler-tool</code>. This is like <code>--copy-bins</code>, but it copies the executables into a <em>compiler-specific location</em>, so a tool built for GHC 8.0.2 will be stored separately from the same tool built for GHC 8.2.2. <code>stack exec</code> arranges for the executables for the current compiler version to end up in the <code>PATH</code>, so you only need to build and install your tools once per compiler version.</p><p>Does this kind of suck? Yes, a little bit, but it sucks a whole lot less than all your editor integration breaking every time you switch to a project that uses a different version of GHC. I use the following command in a fresh sandbox when a Stackage LTS comes out for a new version of GHC:</p><pre><code>$ stack build --copy-compiler-tool ghc-mod hoogle weeder +</code></pre><p>This way, I only have to build those tools once, and I don’t worry about rebuilding them again until a the next release of GHC. To verify that things are working properly, you should be able to create a fresh <code>stack</code> project, run a command like this one, and get a similar result:</p><pre><code>$ stack exec -- which ghc-mod +/Users/alexis/.stack/compiler-tools/x86_64-osx/ghc-8.2.2/bin/ghc-mod +</code></pre><p>Note that this path is scoped to my operating system and my compiler version, but nothing else—no LTS or anything like that.</p><h2><a name="warning-flags-for-a-safe-build"></a>Warning flags for a safe build</h2><p>Haskell is a relatively strict language as programming languages go, but in my experience, it isn’t quite strict enough. Many things are not errors that probably ought to be, like orphan instances and inexhaustive pattern matches. Fortunately, GHC provides <em>warnings</em> that catch these problems statically, which fill in the gaps. I recommend using the following flags on all projects to ensure everything is caught:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat"><code>-Wcompat</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates"><code>-Wincomplete-record-updates</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints"><code>-Wredundant-constraints</code></a></p></li></ul><p>The <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> option turns on <em>most</em> warnings, but (ironically) not all of them. The <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Weverything"><code>-Weverything</code></a> flag truly turns on <em>all</em> warnings, but some of the warnings left disabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> really are quite silly, like warning when type signatures on polymorphic local bindings are omitted. Some of them, however, are legitimately useful, so I recommend turning them on explicitly.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat"><code>-Wcompat</code></a> enables warnings that make your code more robust in the face of future backwards-incompatible changes. These warnings are trivial to fix and serve as free future-proofing, so I see no reason not to turn these warnings on.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates"><code>-Wincomplete-record-updates</code></a> and <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a> are things I think ought to be enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> because they both catch what are essentially partial pattern-matches (and therefore runtime errors waiting to happen). The fact that <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a> <em>isn’t</em> enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> is so surprising that it can lead to bugs being overlooked, since the extremely similar <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-patterns"><code>-Wincomplete-patterns</code></a> <em>is</em> enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a>.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints"><code>-Wredundant-constraints</code></a> is a useful warning that helps to eliminate unnecessary typeclass constraints on functions, which can sometimes occur if a constraint was previously necessary but ends up becoming redundant due to a change in the function’s behavior.</p><p>I put all five of these flags in the <code>.cabal</code> file (or <code>package.yaml</code>), which enables them everywhere, but this alone is unlikely to enforce a warning-free codebase, since the build will still succeed even in the presence of warnings. Therefore, when building projects in CI, I pass the <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Werror"><code>-Werror</code></a> flag (using <code>--ghc-options=-Werror</code> for <code>stack</code>), which treats warnings as errors and halts the build if any warnings are found. This is useful, since it means warnings don’t halt the whole build while developing, making it possible to write some code that has warnings and still run the test suite, but it still enforces that pushed code be warning-free.</p><h2><a name="any-flavor-you-like"></a>Any flavor you like</h2><p>Haskell is both a language and a spectrum of languages. It is both a standard and a specific implementation. Haskell 98 and Haskell 2010 are good, small languages, and there are a few different implementations, but when people talk about “Haskell”, unqualified, they’re almost always talking about GHC.</p><p>GHC Haskell, in stark contrast to standard Haskell, is neither small nor particularly specific, since GHC ships with <em>dozens</em> of knobs and switches that can be used to configure the language. In theory, this is a little terrifying. How could anyone ever hope to talk about Haskell and agree upon how to write it if there are so many <em>different</em> Haskells, each a little bit distinct? Having a cohesive ecosystem would be completely hopeless.</p><p>Fortunately, in practice, this is not nearly as bad as it seems. The majority of GHC extensions are simple switches: a feature is either on or it is off. Turning a feature on rarely affects code that does not use it, so most extensions can be turned on by default, and programmers may simply avoid the features they do not wish to use, just as any programmer in any programming language likely picks a subset of their language’s features to use on a daily basis. Writing Haskell is not different in this regard, only in the sense that it does not allow all features to be used by default; everything from minor syntactic tweaks to entirely new facets of the type system are opt-in.</p><p>Frankly, I think the UX around this is terrible. I recognize the desire to implement a standard Haskell, and the old <code>-fglasgow-exts</code> was not an especially elegant solution for people wishing to use nonstandard Haskell, but having to insert <code>LANGUAGE</code> pragmas at the top of every module just to take advantage of the best features GHC has to offer is a burden, and it is unnecessarily intimidating. I think much of the Haskell community finds the use of <code>LANGUAGE</code> pragmas preferable to enabling extensions globally using the <code>default-extensions</code> list in the <code>.cabal</code> file, but I cut across the grain on that issue <em>hard</em>. The vast majority of language extensions I use are extensions I want enabled all the time; a list of them at the top of a module is just distracting noise, and it only serves to bury the extensions I really do want to enable on a module-by-module basis. It also makes it tricky to communicate with a team which extensions are acceptable (or even preferable) and which are discouraged.</p><p>My <em><strong>strong</strong></em> recommendation if you decide to write GHC Haskell on a team is to agree as a group to a list of extensions the team is happy with enabling everywhere and putting those extensions in the <code>default-extensions</code> list in the <code>.cabal</code> file. This eliminates clutter, busywork, and the conceptual overhead of remembering which extensions are in favor, and which are discouraged. This is a net win, and it isn’t at all difficult to look in the <code>.cabal</code> file when you want to know which extensions are in use.</p><p>Now, with that small digression out of the way, the question becomes precisely which extensions should go into that <code>default-extensions</code> list. I happen to like using most of the features GHC makes available, so I enable a whopping <strong>34</strong> language extensions <em>by default</em>. As of GHC 8.2, here is my list:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable"><code>DeriveFoldable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor"><code>DeriveFunctor</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric"><code>DeriveGeneric</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift"><code>DeriveLift</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable"><code>DeriveTraversable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a></p></li></ul><p>This is a lot, and a few of them are likely to be more controversial than others. Since I do not imagine everyone will agree with everything in this list, I’ve broken it down into smaller chunks, arranged from what I think ought to be least controversial to most controversial, along with a little bit of justification why each extension is in each category. If you’re interested in coming up with your own list of extensions, the rest of this section is for you.</p><h3><a name="trivial-lifting-of-standards-imposed-limitations"></a>Trivial lifting of standards-imposed limitations</h3><p>A few extensions are tiny changes that lift limitations that really have no reason to exist, other than that they are mandated by the standard. I am not sure why these restrictions are in the standard to begin with, other than perhaps a misguided attempt at making the language simpler. These extensions include the following:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a></p></li></ul><p>These extensions have no business <em>not</em> being turned on everywhere. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a> end up being turned on in almost any nontrivial Haskell module, since without them, the typeclass system is pointlessly and artificially limited.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a> is extremely useful, completely safe, and has zero downsides.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a> are almost impossible to avoid, given how many libraries use them, and they are a completely obvious generalization of single-parameter typeclasses. Much like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a>, I see no real reason to ever leave these disabled.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a> is even stranger to me, since <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyDataDecls"><code>EmptyDataDecls</code></a> is in Haskell 2010, so it’s possible to define empty datatypes in standard Haskell but not exhaustively pattern-match on them! This is silly, and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a> should be standard Haskell.</p><h3><a name="syntactic-conveniences"></a>Syntactic conveniences</h3><p>A few GHC extensions are little more than trivial, syntactic abbreviations. These things would be tiny macros in a Lisp, but they need to be extensions to the compiler in Haskell:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a></p></li></ul><p>All of these extensions are only triggered by explicit use of new syntax, so existing programs will never change behavior when these extensions are introduced.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a> only saves a few characters, but it eliminates the need to come up with a fresh, unique variable name that will only be used once, which is sometimes hard to do and leads to worse names overall. Sometimes, it really is better to leave something unnamed.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a> isn’t something I find I commonly need, but when I do, it’s nice to have. It’s far easier to read than nested <code>if...then...else</code> chains, and it uses the existing guard syntax already used with function declarations and <code>case...of</code>, so it’s easy to understand, even to those unfamiliar with the extension.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a> avoids headaches and clutter when using Haskell records without the <a href="https://www.reddit.com/r/haskell/comments/6jaa5f/recordwildcards_and_binary_parsing/djd5ugj/">accidental identifier capture issues</a> of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards"><code>RecordWildCards</code></a>. It’s a nice, safe compromise that brings some of the benefits of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards"><code>RecordWildCards</code></a> without any downsides.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a> is a logical generalization of tuple syntax in the same vein as standard operator sections, and it’s quite useful when using applicative notation. I don’t see any reason to not enable it.</p><h3><a name="extensions-to-the-deriving-mechanism"></a>Extensions to the deriving mechanism</h3><p>GHC’s typeclass deriving mechanism is one of the things that makes Haskell so pleasant to write, and in fact I think Haskell would be nearly unpalatable to write without it. Boilerplate generation is a good thing, since it defines operations in terms of a single source of truth, and generated code is code you do not need to maintain. There is rarely any reason to write a typeclass instance by hand when the deriving mechanism will write it automatically.</p><p>These extensions give GHC’s typeclass deriving mechanism more power without any cost. Therefore, I see no reason <em>not</em> to enable them:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable"><code>DeriveFoldable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor"><code>DeriveFunctor</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric"><code>DeriveGeneric</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift"><code>DeriveLift</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable"><code>DeriveTraversable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a></p></li></ul><p>The first five of these simply extend the list of typeclasses GHC knows how to derive, something that will only ever be triggered if the user explicitly requests GHC derive one of those classes. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a> is quite possibly one of the most important extensions in all of Haskell, since it dramatically improves <code>newtype</code>s’ utility. Wrapper types can inherit instances they need without any boilerplate, and making increased type safety easier and more accessible is always a good thing in my book.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a> is new to GHC 8.2, but it finally presents the functionality of GHC’s <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> extension in a useful way. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> is useful when used with certain libraries that use <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a> (discussed later) with <code>GHC.Generics</code> to derive instances of classes without the deriving being baked into GHC. Unfortunately, enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> essentially disables the far more useful <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a>, so I do <em>not</em> recommend enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a>. Fortunately, with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a>, it’s possible to opt into the <code>anyclass</code> deriving strategy on a case-by-case basis, getting some nice boilerplate reduction in the process.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a> is useful when GHC’s deriving algorithms aren’t <em>quite</em> clever enough to deduce the instance context automatically, so it allows specifying it manually. This is only useful in a few small situations, but it’s nice to have, and there are no downsides to enabling it, so it ought to be turned on.</p><h3><a name="lightweight-syntactic-adjustments"></a>Lightweight syntactic adjustments</h3><p>A couple extensions tweak Haskell’s syntax in more substantial ways than things like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a>, but not in a significant enough way for them to really be at all surprising:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a> mirror strictness annotations on datatypes, so they are unlikely to be confusing, and they provide a much more pleasant notation for annotating the strictness of bindings than explicit uses of <code>seq</code>.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a> are also fairly self-explanatory: they’re just like type annotations, but for types instead of values. Writing kind signatures explicitly is usually unnecessary, but they can be helpful for clarity or for annotating phantom types when <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPolyKinds"><code>PolyKinds</code></a> is not enabled. Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a> doesn’t have any adverse effects, so I see no reason not to enable it everywhere.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a> adjusts the syntax of types slightly, allowing operators to be used as type constructors and written infix, which is technically backwards-incompatible, but I’m a little suspicious of anyone using <code>(!@#$)</code> as a type variable (especially since standard Haskell does not allow them to be written infix). This extension is useful with some libraries like <code>natural-transformations</code> that provide infix type constructors, and it makes the type language more consistent with the value language.</p><h3><a name="polymorphic-string-literals"></a>Polymorphic string literals</h3><p>I’m putting this extension in a category all of its own, mostly because I don’t think any other Haskell extensions have quite the same set of tradeoffs:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a></p></li></ul><p>For me, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is not optional. Haskell’s infamous “string problem” (discussed in more detail at the end of this blog post) means that <code>String</code> is a linked list of characters, and all code that cares about performance actually uses <code>Text</code>. Manually invoking <code>pack</code> on every single string literal in a program is just noise, and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> solves that noise.</p><p>That said, I actually find I don’t use the polymorphism of string literals very often, and I’d be alright with monomorphic literals if I could make them <em>all</em> have type <code>Text</code>. Unfortunately, there isn’t a way to do this, so <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is the next best thing, even if it sometimes causes some unnecessary ambiguities that require type annotations to resolve.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is an extension that I use so frequently, in so many modules (especially in my test suites) that I would rather keep it on everywhere so I don’t have to care about whether or not it’s enabled in the module I’m currently writing. On the other hand, it certainly isn’t my favorite language extension, either. I wouldn’t go as far as to call it a necessary evil, since I don’t think it’s truly “evil”, but it does seem to be necessary.</p><h3><a name="simple-extensions-to-aid-type-annotation"></a>Simple extensions to aid type annotation</h3><p>The following two extensions significantly round out Haskell’s language for referring to types, making it much easier to insert type annotations where necessary (for removing ambiguity or for debugging type errors):</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a></p></li></ul><p>That the behavior of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a> is <em>not</em> the default is actually one of the most common gotchas for new Haskellers. Sadly, it can theoretically adjust the behavior of existing Haskell programs, so I cannot include it in the list of trivial changes, but I would argue such programs were probably confusing to begin with, and I have never seen a program in practice that was impacted by that problem. I think leaving <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a> off is much, much more likely to be confusing than turning it on.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a> is largely unrelated, but I include it in this category because it’s quite useful and cooperates well with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a>. Use of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a> makes instantiation much more lightweight than full-blown type annotations, and once again, it has no downsides if it is enabled and unused (since it is a syntactic addition). I recommend enabling it.</p><h3><a name="simple-extensions-to-the-haskell-type-system"></a>Simple extensions to the Haskell type system</h3><p>A few extensions tweak the Haskell type system in ways that I think are simple enough to be self-explanatory, even to people who might not have known they existed. These are as follows:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a> is largely just used to define typeclass aliases, which is both useful and self-explanatory. Unifying the type and constraint language also has the effect of allowing type-level programming with constraints, which is sometimes useful, but far rarer in practice than the aforementioned use case.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a> are uncommon, looking at the average type in a Haskell program, but they’re certainly nice to have when you need them. The idea of pushing <code>forall</code>s further into a type to adjust how variables are quantified is something that I find people find fairly intuitive, especially after seeing them used once or twice, and higher-rank types do crop up regularly, if infrequently.</p><h3><a name="intermediate-syntactic-adjustments"></a>Intermediate syntactic adjustments</h3><p>Three syntactic extensions to Haskell are a little bit more advanced than the ones I’ve already covered, and none of them are especially related:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is, on the surface, simple. It changes <code>do</code> notation to use <code>Applicative</code> operations where possible, which allows using <code>do</code> notation with applicative functors that are not monads, and it also makes operations potentially more performant when <code>(&lt;*&gt;)</code> can be implemented more efficiently than <code>(&gt;&gt;=)</code>. In theory, it sounds like there are no downsides to enabling this everywhere. However, there are are a few drawbacks that lead me to put it so low on this list:</p><ol><li><p>It considerably complicates the desugaring of <code>do</code> blocks, to the point where the algorithm cannot even be easily syntactically documented. In fact, an additional compiler flag, <code>-foptimal-applicative-do</code>, is a way to <em>opt into</em> optimal solutions for <code>do</code> block expansions, tweaking the desugaring algorithm to have an <em>O</em>(<em>n</em><sup>3</sup>) time complexity! This means that the default behavior is guided by a heuristic, and desugaring isn’t even especially predictable. This isn’t necessarily so bad, since it’s really only intended as an optimization when some <code>Monad</code> operations are still necessary, but it does dramatically increase the complexity of one of Haskell’s core forms.</p></li><li><p>The desugaring, despite being <em>O</em>(<em>n</em><sup>2</sup>) by default, isn’t even especially clever. It relies on a rather disgusting hack that recognizes <code>return e</code>, <code>return $ e</code>, <code>pure e</code>, or <code>pure $ e</code> expressions <em>syntactically</em>, and it completely gives up if an expression with precisely that shape is not the final statement in a <code>do</code> block. This is a bit awkward, since it effectively turns <code>return</code> and <code>pure</code> into syntax when before they were merely functions, but that isn’t all. It also means that the following <code>do</code> block is <em>not</em> desugared using <code>Applicative</code> operations:</p><pre><code class="pygments"><span class="kr">do</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">t</span> +<span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="n">z</span></code></pre><p>This will use the normal, monadic desugaring, despite the fact that it is trivially desugared into <code>Applicative</code> operations as <code>foo a b *&gt; bar s t *&gt; baz y z</code>. In order to get <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> to trigger here, the <code>do</code> block must be contorted into the following:</p><pre><code class="pygments"><span class="kr">do</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">t</span> +<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="n">z</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">r</span></code></pre><p>This seems like an odd oversight.</p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> doesn’t seem able to cope with <code>do</code> blocks when <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is enabled. I reported this as <a href="https://ghc.haskell.org/trac/ghc/ticket/14471">an issue on the GHC bug tracker</a>, but it hasn’t received any attention, so it’s not likely to get fixed unless someone takes the initiative to do so.</p></li><li><p>Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> can cause problems with code that may have assumed <code>do</code> would always be monadic, and sometimes, that can cause code that typechecks to lead to an infinite loop at runtime. Specifically, if <code>do</code> notation is used to define <code>(&lt;*&gt;)</code> in terms of <code>(&gt;&gt;=)</code>, enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> will cause the definition of <code>(&lt;*&gt;)</code> to become self-referential and therefore divergent. Fortunately, this issue can be easily mitigated by simply writing <code>(&lt;*&gt;) = ap</code> instead, which is clearer and shorter than the equivalent code using <code>do</code>.</p></li></ol><p>Given all these things, it seems <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is a little too new in a few places, and it isn’t quite baked. Still, I keep it enabled by default. Why? Well, <em>usually</em> it works fine without any problems, and when I run into issues, I can disable it on a per-module basis by writing <code>{-# LANGUAGE NoApplicativeDo #-}</code>. I still find that keeping it enabled by default is fine the vast majority of the time, I just sometimes need to work around the bugs.</p><p>In contrast, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a> isn’t buggy at all, as far as I can tell, it’s just not usually useful without fairly advanced features like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> (for type equalities) or <code>GHC.Generics</code>. I mostly use it for <a href="/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/">making lifting instances for <code>mtl</code>-style typeclasses easier to write</a>, which I’ve found to be a tiny bit tricky to explain (mostly due to the use of type equalities in the context), but it works well. I don’t see any real reason to leave this disabled, but if you don’t think you’re going to use it anyway, it doesn’t really matter one way or the other.</p><p>Finally, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a> allow users to extend the pattern language just as they are allowed to extend the value language. Bidirectional pattern synonyms are isomorphisms, and it’s quite useful to allow those isomorphisms to be used with Haskell’s usual pattern-matching syntax. I think this extension is actually quite benign, but I put it so low on this list because it seems infrequently used, and I get the sense most people consider it fairly advanced. I would argue, however, that it’s a very pleasant, useful extension, and it’s no more complicated than a number of the features in Haskell 98.</p><h3><a name="intermediate-extensions-to-the-haskell-type-system"></a>Intermediate extensions to the Haskell type system</h3><p>Now we’re getting into the meat of things. Everything up to this point has been, in my opinion, completely self-evident in its usefulness and simplicity. As far as I’m concerned, the extensions in the previous six sections have no business ever being left disabled. Starting in this section, however, I could imagine a valid argument being made either way.</p><p>The following three extensions add some complexity to the Haskell type system in return for some added expressive power:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> are related, given that the former is subsumed by the latter, but <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> also enables an alternative syntax. Both syntaxes allow packing away a typeclass dictionary or equality constraint that is brought into scope upon a successful pattern-match against a data constructor, something that is sometimes quite useful but certainly a departure from Haskell’s simple ADTs.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a> extend multi-parameter typeclasses, and they are almost unavoidable, given their use in the venerable <code>mtl</code> library. Like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a>, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a> add an additional layer of complexity to the typeclass system in order to express certain things that would otherwise be difficult or impossible.</p><p>All of these extensions involve a tradeoff. Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> also implies <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds"><code>MonoLocalBinds</code></a>, which disables let generalization, one of the most likely ways a program that used to typecheck might subsequently fail to do so. Some might argue that this is a good reason to turn <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> on in a per-module basis, but I disagree: I actually want my language to be fairly consistent, and given that I know I am likely going to want to use <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> <em>somewhere</em>, I want <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds"><code>MonoLocalBinds</code></a> enabled <em>everywhere</em>, not inconsistently and sporadically.</p><p>That aside, all these extensions are relatively safe. They are well-understood, and they are fairly self-contained extensions to the Haskell type system. I think these extensions have a very good power to cost ratio, and I find myself using them regularly (especially <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a>), so I keep them enabled globally.</p><h3><a name="advanced-extensions-to-the-haskell-type-system"></a>Advanced extensions to the Haskell type system</h3><p>Finally, we arrive at the last set of extensions in this list. These are the most advanced features Haskell’s type system currently has to offer, and they are likely to be the most controversial to enable globally:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a></p></li></ul><p>All of these extensions exist exclusively for the purpose of type-level programming. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a> allows datatype promotion, creating types that are always uninhabited and therefore can only be used phantom. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> allows the definition of type-level functions that map types to other types. Both of these are minor extensions to Haskell’s surface area, but they have rather significant ramifications on the sort of programming that can be done and the way GHC’s typechecker must operate.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> is an interesting extension because it comes in so many flavors: associated type synonyms, associated datatypes, open and closed type synonym families, and open and closed datatype families. Associated types tend to be easier to grok and easier to use, though they can also be replaced by functional dependencies. Open type families are also quite similar to classes and instances, so they aren’t <em>too</em> tricky to understand. Closed type families, on the other hand, are a rather different beast, and they can be used to do fairly advanced things, <em>especially</em> in combination with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a>.</p><p>I happen to appreciate GHC’s support for these features, and while I’m hopeful that an eventual <code>DependentHaskell</code> will alleviate many of the existing infelicities with dependently typed programming in GHC, in the meantime, it’s often useful to enjoy what exists where practically applicable. Therefore, I have little problem keeping them enabled, since, like the vast majority of extensions on this list, these extensions merely lift restrictions, not adjust semantics of the language without the extensions enabled. When I am going to write a type family, I am going to turn on <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a>; I see no reason to annotate the modules in which I decide to do so. I do not write an annotation at the top of each module in which I define a typeclass or a datatype, so why should I do so with type families?</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a> is a little bit different, since it’s a very new extension, and it doesn’t seem to always work as well as I would hope. Still, when it doesn’t work, it fails with a very straightforward error message, and when it works, it is legitimately useful, so I don’t see any real reason to leave it off if <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> is enabled.</p><h3><a name="extensions-intentionally-left-off-this-list"></a>Extensions intentionally left off this list</h3><p>Given what I’ve said so far, it may seem like I would advocate flipping on absolutely every lever GHC has to offer, but that isn’t actually true. There are a few extensions I quite intentionally do <em>not</em> enable.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XUndecidableInstances"><code>UndecidableInstances</code></a> is something I turn on semi-frequently, since GHC’s termination heuristic is not terribly advanced, but I turn it on per-module, since it’s useful to know when it’s necessary (and in application code, it rarely is). <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverlappingInstances"><code>OverlappingInstances</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XIncoherentInstances"><code>IncoherentInstances</code></a>, in contrast, are completely banned—not only are they almost always a bad idea, GHC has a better, more fine-grained way to opt into overlapping instances, using the <code>{-# OVERLAPPING #-}</code>, <code>{-# OVERLAPPABLE #-}</code>, and <code>{-# INCOHERENT #-}</code> pragmas.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XQuasiQuotes"><code>QuasiQuotes</code></a> are tricky ones. Anecdotes seem to suggest that enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> everywhere leads to worse compile times, but after trying this on a few projects and measuring, I wasn’t able to detect any meaningful difference. Unless I manage to come up with some evidence that these extensions actually slow down compile times just by being <em>enabled</em>, even if they aren’t used, then I may add them to my list of globally-enabled extensions, since I use them regularly.</p><p>Other extensions I haven’t mentioned are probably things I just don’t use very often and therefore haven’t felt the need to include on this list. It certainly isn’t exhaustive, and I add to it all the time, so I expect I will continue to do so in the future. This is just what I have for now, and if your favorite extension isn’t included, it probably isn’t a negative judgement against that extension. I just didn’t think to mention it.</p><h2><a name="libraries-a-field-guide"></a>Libraries: a field guide</h2><p>Now that you’re able to build a Haskell project and have chosen which handpicked flavor of Haskell you are going to write, it’s time to decide which libraries to use. Haskell is an expressive programming language, and the degree to which different libraries can shape the way you structure your code is significant. Picking the right libraries can lead to clean code that’s easy to understand and maintain, but picking the wrong ones can lead to disaster.</p><p>Of course, there are <em>thousands</em> of Haskell libraries on Hackage alone, so I cannot hope to cover all of the ones I have ever found useful, and I certainly cannot cover ones that would be useful but I did not have the opportunity to try (of which there are certainly many). This blog post is long enough already, so I’ll just cover a few categories of libraries that I think I can offer interesting commentary on; most libraries can generally speak for themselves.</p><h3><a name="having-an-effect"></a>Having an effect</h3><p>One of the first questions Haskell programmers bump into when they begin working on a large application is how they’re going to model effects. Few practical programming languages are pure, but Haskell is one of them, so there’s no getting away from coming up with a way to manage side-effects.</p><p>For some applications, Haskell’s built-in solution might be enough: <code>IO</code>. This can work decently for data processing programs that do very minimal amounts of I/O, and the types of side-effects they perform are minimal. For these applications, most of the logic is likely to be pure, which means it’s already easy to reason about and easy to test. For other things, like web applications, it’s more likely that a majority of the program logic is going to be side-effectful by its nature—it may involve making HTTP requests to other services, interacting with a database, and writing to logfiles.</p><p>Figuring out how to structure these effects in a type-safe, decoupled, composable way can be tricky, especially since Haskell has so many different solutions. I could not bring myself to choose just one, but I did choose two: the so-called “<code>mtl</code> style” and freer monads.</p><p><code>mtl</code> style is so named because it is inspired by the technique of interlocking monadic typeclasses and lifting instances used to model effects using constraints that is used in the <a href="https://hackage.haskell.org/package/mtl"><code>mtl</code></a> library. Here is a small code example of what <code>mtl</code> style typeclasses and handlers look like:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MaybeT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="p">(</span><span class="s">"readFile: no such file "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="p">)</span> + +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">delete</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="n">vfs</span></code></pre><p>This is the most prevalent way to abstract over effects in Haskell, and it’s been around for a long time. Due to the way it uses the typeclass system, it’s also very fast, since GHC can often specialize and inline the typeclass dictionaries to avoid runtime dictionary passing. The main drawbacks are the amount of boilerplate required and the conceptual difficulty of understanding exactly how monad transformers, monadic typeclasses, and lifting instances all work together to discharge <code>mtl</code> style constraints.</p><p>There are various alternatives to <code>mtl</code>’s direct approach to effect composition, most of which are built around the idea of reifying a computation as a data structure and subsequently interpreting it. The most popular of these is the <code>Free</code> monad, a clever technique for deriving a monad from a functor that happens to be useful for modeling programs. Personally, I think <code>Free</code> is overhyped. It’s a cute, mathematically elegant technique, but it involves a lot of boilerplate, and composing effect algebras is still a laborious process. The additional expressive power of <code>Free</code>, namely its ability to choose an interpreter dynamically, at runtime, is rarely necessary or useful, and it adds complexity and reduces performance for few benefits. (And in fact, this is still possible to do with <code>mtl</code> style, it’s just uncommon because there is rarely any need to do so.)</p><p>A 2017 blog post entitled <a href="https://markkarpov.com/post/free-monad-considered-harmful.html">Free monad considered harmful</a> discussed <code>Free</code> in comparison with <code>mtl</code> style, and unsurprisingly cast <code>Free</code> in a rather unflattering light. I largely agree with everything outlined in that blog post, so I will not retread its arguments here. I do, however, think that there is another abstraction that <em>is</em> quite useful: the so-called “freer monad” used to implement extensible effects.</p><p>Freer moves even further away from worrying about functors and monads, since its effect algebras do not even need to be functors. Instead, freer’s effect algebras are ordinary GADTs, and reusable, composable effect handlers are easily written to consume elements of these datatypes. Unfortunately, the way this works means that GHC is still not clever enough to optimize freer monads as efficiently as <code>mtl</code> style, since it can’t easily detect when the interpreter is chosen statically and use that information to specialize and inline effect implementations, but the cost difference is significantly reduced, and I’ve found that in real application code, the vast majority of the cost does not come from the extra overhead introduced by a more expensive <code>(&gt;&gt;=)</code>.</p><p>There are a few different implementations of freer monads, but I, sadly, was not satisfied with any of them, so I decided to contribute to the problem by creating yet another one. My implementation is called <a href="https://hackage.haskell.org/package/freer-simple"><code>freer-simple</code></a>, and it includes a streamlined API with <a href="https://hackage.haskell.org/package/freer-simple-1.0.1.1/docs/Control-Monad-Freer.html">more documentation than any other freer implementation</a>. Writing the above <code>mtl</code> style example using <code>freer-simple</code> is more straightforward:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="nb">()</span> + +<span class="nf">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Member</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">send</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span> + +<span class="nf">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Member</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">send</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="nf">runFileSystemIO</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LastMember</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span> +<span class="nf">runFileSystemIO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">interpretM</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="kr">case</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="nf">runFileSystemInMemory</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">effs</span> +<span class="nf">runFileSystemInMemory</span><span class="w"> </span><span class="n">initVfs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runState</span><span class="w"> </span><span class="n">initVfs</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">State</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span> +<span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">reinterpret</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">case</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="p">(</span><span class="s">"readFile: no such file "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="p">)</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">delete</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="n">vfs</span></code></pre><p>(It could be simplified further with a little bit of Template Haskell to generate the <code>readFile</code> and <code>writeFile</code> function definitions, but I haven’t gotten around to writing that.)</p><p>So which effect system do I recommend? I used to recommend <code>mtl</code> style, but as of only two months ago, I now recommend <code>freer-simple</code>. It’s easier to understand, involves less boilerplate, achieves “good enough” performance, and generally gets out of the way wherever possible. Its API is designed to make it easy to do the sorts of the things you most commonly need to do, and it provides a core set of effects that can be used to build a real-world application.</p><p>That said, freer is indisputably relatively new and relatively untested. It has success stories, but <code>mtl</code> style is still the approach used by the majority of the ecosystem. <code>mtl</code> style has more library support, its performance characteristics are better understood, and it is a tried and true way to structure effects in a Haskell application. If you understand it well enough to use it, and you are happy with it in your application, my recommendation is to stick with it. If you find it confusing, however, or you end up running up against its limits, give <code>freer-simple</code> a try.</p><h3><a name="through-the-looking-glass-to-lens-or-not-to-lens"></a>Through the looking glass: to lens or not to lens</h3><p>There’s no getting around it: <a href="https://hackage.haskell.org/package/lens"><code>lens</code></a> is a behemoth of a library. For a long time, I wrote Haskell without it, and honestly, it worked out alright. I just wasn’t doing a whole lot of work that involved complicated, deeply-nested data structures, and I didn’t feel the need to bring in a library with such a reputation for having impenetrable operators and an almost equally impenetrable learning curve.</p><p>But, after some time, I decided I wanted to take the plunge. So I braced myself for the worst, pulled out my notebook, and started writing some code. To my surprise… it wasn’t that hard. It made sense. Sure, I still don’t know how it works on the inside, and I never did learn the majority of the exports in <code>Control.Lens.Operators</code>, but I had no need to. Lenses were useful in the way I had expected them to be, and so were prisms. One thing led to another, and before long, I understood the relationship between the various optics, the most notable additions to my toolkit being folds and traversals. Sure, the type errors were completely opaque much of the time, but I was able to piece things together with ample type annotations and time spent staring at ill-typed expressions. Before long, I had developed an intuition for <code>lens</code>.</p><p>After using it for a while, I retrospected on whether or not I liked it, and honestly, I still can’t decide. Some lensy expressions were straightforward to read and were a pleasant simplification, like this one:</p><pre><code class="pygments"><span class="nf">paramSpecs</span><span class="w"> </span><span class="o">^..</span><span class="w"> </span><span class="n">folded</span><span class="o">.</span><span class="n">_Required</span></code></pre><p>Others were less obviously improvements, such as this beauty:</p><pre><code class="pygments"><span class="kt">M</span><span class="o">.</span><span class="n">fromList</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">paramSpecs</span><span class="w"> </span><span class="o">^..</span><span class="w"> </span><span class="n">folded</span><span class="o">.</span><span class="n">_Optional</span><span class="o">.</span><span class="n">filtered</span><span class="w"> </span><span class="p">(</span><span class="n">has</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">_2</span><span class="o">.</span><span class="n">_UsePreviousValue</span><span class="p">)</span></code></pre><p>But operator soup aside, there was something deeper about <code>lens</code> that bothered me, and I just wasn’t sure what. I didn’t know how to articulate my vague feelings until I read a 2014 blog post entitled <a href="https://ro-che.info/articles/2014-04-24-lens-unidiomatic">Lens is unidiomatic Haskell</a>, which includes a point that I think is spot-on:</p><blockquote><p>Usually, types in Haskell are rigid. This leads to a distinctive style of composing programs: look at the types and see what fits where. This is impossible with <code>lens</code>, which takes overloading to the level mainstream Haskell probably hasn’t seen before.</p><p>We have to learn the new language of the <code>lens</code> combinators and how to compose them, instead of enjoying our knowledge of how to compose Haskell functions. Formally, <code>lens</code> types are Haskell function types, but while with ordinary Haskell functions you immediately see from types whether they can be composed, with <code>lens</code> functions this is very hard in practice.</p><p>[…]</p><p>Now let me clarify that this doesn’t necessarily mean that <code>lens</code> is a bad library. It’s an <em>unusual</em> library. It’s almost a separate language, with its own idioms, embedded in Haskell.</p></blockquote><p>The way <code>lens</code> structures its types deliberately introduces a sort of subtyping relationship—for example, all lenses are traversals and all traversals are folds, but not vice versa—and indeed, knowing this subtyping relationship is essential to working with the library and understanding how to use it. It is helpfully documented with a large diagram on <a href="https://hackage.haskell.org/package/lens">the <code>lens</code> package overview page</a>, and that diagram was most definitely an invaluable resource for me when I was learning how to use the library.</p><p>On the surface, this isn’t unreasonable. Subtyping is an enormously useful concept! The only reason Haskell dispenses with it entirely is because it makes type inference notoriously difficult. The subtyping relation between optics is one of the things that makes them so useful, since it allows you to easily compose a lens with a prism and get a traversal out. Unfortunately, the downside of all this is that Haskell does not truly have subtyping, so all of <code>lens</code>’s “types” really must be type aliases for types of roughly the same shape, namely functions. This makes type errors completely <em>baffling</em>, since the errors do not mention the aliases, only the fully-expanded types (which are often rather complicated, and their meaning is not especially clear without knowing how <code>lens</code> works under the hood).</p><p>So the above quote is correct: working with <code>lens</code> really <em>is</em> like working in a separate embedded language, but I’m usually okay with that. Embedded, domain-specific languages are good! Unfortunately, in this case, the host language is not very courteous to its guest. Haskell does not appear to be a powerful enough language for <code>lens</code> to be a language in its own right, so it must piggyback on top of Haskell’s error reporting mechanisms, which are insufficient for <code>lens</code> to be a cohesive linguistic abstraction. Just as debugging code by stepping through the assembly it produces (or, perhaps more relevant in 2018, debugging a compile-to-JS language by looking at the emitted JavaScript instead of the source code) makes for an unacceptably leaky language. We would never stand for such a thing in our general-purpose language tooling, and we should demand better even in our embedded languages.</p><p>That said, <code>lens</code> is just too useful to ignore. It is a hopelessly leaky abstraction, but it’s still an abstraction, and a powerful one at that. Given my selection of default extensions as evidence, I think it’s clear I have zero qualms with “advanced” Haskell; I will happily use even <code>singletons</code> where it makes sense. Haskell’s various language extensions are sometimes confusing in their own right, but their complexity is usually fundamental to the expressive power they bring. <code>lens</code> has some fundamental complexity, too, but it is mostly difficult for the wrong reasons. Still, while it is not the first library I reach for on every new Haskell project, manipulating nested data without <code>lens</code> is just too unpleasant after tasting the nectar, so I can’t advise against it in good faith.</p><p>Sadly, this means I’m a bit wishy-washy when it comes to using <code>lens</code>, but I do have at least one recommendation: if you decide to use <code>lens</code>, it’s better to go all-in. Don’t generate lenses for just a handful of datatypes, do it for <em>all</em> of them. You can definitely stick to a subset of the <code>lens</code> library’s features, but don’t apply it in some functions but not others. Having too many different, equally valid ways of doing things leads to confusion and inconsistency, and inconsistency minimizes code reuse and leads to duplication and spaghetti. Commit to using <code>lens</code>, or don’t use it at all.</p><h3><a name="mitigating-the-string-problem"></a>Mitigating the string problem</h3><p>Finally, Haskell has a problem with strings. Namely, <code>String</code> is a type alias for <code>[Char]</code>, a lazy, singly linked list of characters, which is an awful representation of text. Fortunately, the answer to this problem is simple: ban <code>String</code> in your programs.</p><p>Use <code>Text</code> everywhere. I don’t really care if you pick strict <code>Text</code> or lazy <code>Text</code>, but pick one and stick to it. Don’t ever use <code>String</code>, and <em>especially</em> don’t ever, <em>ever</em>, <em><strong>ever</strong></em> use <code>ByteString</code> to represent text! There are enormously few legitimate cases for using <code>ByteString</code> in a program that is not explicitly about reading or writing raw data, and even at that level, <code>ByteString</code> should only be used at program boundaries. In that sense, I treat <code>ByteString</code> much the same way I treat <code>IO</code>: push it to the boundaries of your program.</p><p>One of Haskell’s core tenets is making illegal states unrepresentable. Strings are not especially useful datatypes for this, since they are sequences of arbitrary length made up of atoms that can be an enormously large number of different things. Still, string types enforce a very useful invariant, a notion of a sequence of human-readable characters. In the presence of Unicode, this is a more valuable abstraction than it might seem, and the days of treating strings as little different from sequences of bytes are over. While strings make a poor replacement for enums, they are quite effective at representing the incredible amount of text humans produce in a staggeringly large number of languages, and they are the right type for that job.</p><p><code>ByteString</code>, on the other hand, is essentially never the right type for any job. If a type classifies a set of values, <code>ByteString</code> is no different from <code>Any</code>. It is the structureless type, the all-encompassing blob of bits. A <code>ByteString</code> could hold anything at all—some text, an image, an executable program—and the type system certainly isn’t going to help to answer that question. The only use case I can possibly imagine for passing around a <code>ByteString</code> in your program rather than decoding it into a more precise type is if it truly holds opaque data, e.g. some sort of token or key provided by a third party with no structure guaranteed whatsoever. Still, even this should be wrapped in a <code>newtype</code> so that the type system enforces this opaqueness.</p><p>Troublingly, <code>ByteString</code> shows up in many libraries’ APIs where it has no business being. In many cases, this seems to be things where ASCII text is expected, but this is hardly a good reason to willingly accept absolutely anything and everything! Make an <code>ASCII</code> type that forbids non-ASCII characters, and provide a <code>ByteString -&gt; Maybe ASCII</code> function. Alternatively, think harder about your problem in question to properly support Unicode as you almost certainly ought to.</p><p>Other places <code>ByteString</code> appears are similarly unfortunate. Base-64 encoding, for example, could be given the wonderfully illustrative type <code>ByteString -&gt; Text</code>, or even <code>ByteString -&gt; ASCII</code>! Such a type makes it immediately clear why base-64 is useful: it allows transforming arbitrary binary data into a reliable textual encoding. If we consider that <code>ByteString</code> is essentially <code>Any</code>, this function has the type <code>Any -&gt; ASCII</code>, which is amazingly powerful! We can convert <em>anything</em> to ASCII text!</p><p>Existing libraries, however, just provide the boring, disappointingly inaccurate type <code>ByteString -&gt; ByteString</code>, which is one of the most useless types there is. It is essentially <code>Any -&gt; Any</code>, the meaningless function type. It conveys nothing about what it does, other than that it is pure. Giving a function this type is scarcely better than dynamic typing. Its mere existence is a failure of Haskell library design.</p><p>But wait, it gets worse! <code>Data.Text.Encoding</code> exports a function called <code>decodeUtf8</code>, which has type <code>ByteString -&gt; Text</code>. What an incredible function with a captivating type! Whatever could it possibly do? Again, this function’s type is basically <code>Any -&gt; Text</code>, which is remarkable in the power it gives us. Let’s try it out, shall we?</p><pre><code>ghci&gt; decodeUtf8 "\xc3\x28" +"*** Exception: Cannot decode byte '\x28': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream +</code></pre><p>Oh. Well, that’s a disappointment.</p><p>Haskell’s string problem goes deeper than <code>String</code> versus <code>Text</code>; it seems to have wound its way around the collective consciousness of the Haskell community and made it temporarily forget that it cares about types and totality. This isn’t that hard, I swear! I can only express complete befuddlement at how many of these APIs are just completely worthless.</p><p>Fortunately, there is a way out, and that way out is <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a>. It is the first Haskell library I ever wrote. It provides <em>type safe</em>, <em>total</em> conversions between <code>Text</code> and various other types, and it is encoding aware. It provides appropriately-typed base-16 and base-64 conversion functions, and is guaranteed to never raise any exceptions. Use it, and apply the Haskell philosophy to your strings, just as you already do for everything else in your program.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p><em>Phew.</em></p><p>When I started writing this blog post, it used the phrase “short overview” in the introduction. It is now over ten thousand words long. I think that’s all I have it in me to say for now.</p><p>Haskell is a wonderful language built by a remarkable group of people. Its community is often fraught with needlessly inflammatory debates about things like the value of codes of conduct, the evils of Hackage revisions, and precisely how much or how little people ought to care about the monad laws. These flame wars frustrate me to no end, and they sometimes go so far as to make me ashamed to call myself a part of the Haskell community. Many on the “outside” seem to view Haskellers as an elitist, mean-spirited cult, more interested in creating problems for itself than solving them.</p><p>That perception is categorically wrong.</p><p>I have never been in a community of programmers so dedicated and passionate about applying thought and rigor to building software, then going out and <em>actually doing it</em>. I don’t know anywhere else where a cutting-edge paper on effect systems is discussed by the very same people who are figuring out how to reliably deploy distributed services to AWS. Some people view the Haskell community as masturbatory, and to some extent, they are probably right. One of my primary motivators for writing Haskell is that it is fun and it challenges me intellectually in ways that other languages don’t. But that challenge is not a sign of uselessness, it is a sign that Haskell is <em>so close</em> to letting me do the right thing, to solving the problem the right way, to letting me work without compromises. When I write in most programming languages, I must constantly accept that my program will never be robust in all the ways I want it to be, and I might as well give up before I even start. Haskell’s greatest weakness is that it tempts me to try.</p><p>Haskell is imperfect, as it will always be. I doubt I will ever be satisfied by any language or any ecosystem. There will always be more to learn, more to discover, better tools and abstractions to develop. Many of them will not look anything like Haskell; they may not involve formal verification or static types or effect systems at all. Perhaps live programming, structural editors, and runtime hotswapping will finally take over the world, and we will find that the problems we thought we were solving were irrelevant to begin with. I can’t predict the future, and while I’ve found great value in the Haskell school of program construction, I dearly hope that we do not develop such tunnel vision that we cannot see that there may be other ways to solve these problems. Many of the solutions are things we likely have not even begun to think about. Still, whether that happens or not, it is clear to me that Haskell is a point in the design space unlike any other, and we learn almost as much from the things it gets wrong as we do from the things it gets right.</p><p>It’s been a wonderful two years, Haskell. I won’t be a stranger.</p><ol class="footnotes"></ol></article>A space of their own: adding a type namespace to Hacketthttps://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/https://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/27 Oct 2017<article><p>As previously discussed on this blog, <a href="https://github.com/lexi-lambda/hackett">my programming language, Hackett</a>, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?</p><p>For now, at least, the answer is that Hackett will emulate Haskell: <strong>Hackett now has two namespaces</strong>. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.</p><h2><a name="why-two-namespaces"></a>Why two namespaces?</h2><p>Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.</p><p>This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="ss">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="ss">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="ss">c</span><span class="p">)))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly <em>annotate</em> when they wish to use a value from a different namespace:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">mapcar</span><span class="w"> </span><span class="nf">#&#39;</span><span class="nb">car</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="nv">c</span><span class="p">)))</span> +<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>The Common Lisp <code>#'x</code> reader abbreviation is equivalent to <code>(function x)</code>, and <code>function</code> is a special form that references a value in the function namespace.</p><p>While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.</p><p>However, Hackett is a little different from all of the aforementioned languages because Hackett has <em>types</em>. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is <em>always</em> syntactically unambiguous.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Even if types and values live in separate namespaces, there is no need for a <code>type</code> form a la CL’s <code>function</code> because it can always be determined implicitly.</p><p>For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span></code></pre><p>This defines a binding named <code>Tuple</code> at the type level, which is a <em>type constructor</em> of two arguments that produces a type of kind <code>*</code>,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> and another binding named <code>Tuple</code> at the value level, which is a <em>value constructor</em> of two arguments that produces a value of type <code>(Tuple a b)</code>.</p><p>But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor <code>tuple</code> instead of <code>Tuple</code>? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>This works fine. But what happens if the programmer decides to change the name of the <code>bar</code> value?</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">qux</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>Can you spot the bug? Disturbingly, this code <em>still compiles</em>! Even though <code>bar</code> is not a member of <code>Foo</code> anymore, it’s still a valid pattern, since names used as patterns match anything, just as the <code>y</code> pattern matches against any integer inside the <code>baz</code> constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: <code>(foo-&gt;integer (baz 42))</code> will still produce <code>0</code>, not <code>42</code>, since the first case always matches.</p><p>Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the <code>Tuple</code> example above, which is illegal if a programming language only supports a single namespace.</p><p>Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The <code>Tuple</code> example from above is perfectly legal Hackett.</p><h2><a name="adding-namespaces-to-a-language"></a>Adding namespaces to a language</h2><p>Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to <em>implement</em> such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?</p><p>Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: <strong>the remainder of this blog post is <em>highly technical</em>, and some of it involves some of the more esoteric corners of Racket’s macro system</strong>. This blog post is <em>not</em> representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.</p><h3><a name="namespaces-as-scopes"></a>Namespaces as scopes</h3><p>With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as <a href="https://www.cs.utah.edu/plt/scope-sets/"><em>sets of scopes</em></a>, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.</p><p>Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the <em>value scope</em>, and all identifiers in type position must have a different scope, which we will call the <em>type scope</em>. How do we create these scopes and apply them to identifiers? In Racket, we use a function called <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29"><code>make-syntax-introducer</code></a>, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can <em>add</em> the scope to all pieces of the syntax object, <em>remove</em> the scope, or <em>flip</em> the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call <code>make-syntax-introducer</code> once for each namespace:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>We define these in a <code>begin-for-syntax</code> block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Each of these two forms is like <code>begin</code>, which is a Racket form that is, for our purposes, essentially a no-op, but it applies <code>value-introducer</code> or <code>type-introducer</code> to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">value-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">type-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span></code></pre><p>This program produces the following output:</p><pre><code>'value-x +'type-x +</code></pre><p>It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.</p><p>However, although this is close, it isn’t <em>quite</em> right. What happens if we nest the two inside each other?</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span></code></pre><pre><code>x: identifier's binding is ambiguous + context...: + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) + #(190359 intdef) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) +</code></pre><p>Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of <code>x</code> is <em>ambiguous</em> because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of <code>begin/value</code> or <code>begin/type</code> to <em>override</em> outer ones, ensuring that a use can only be in a single namespace at a time.</p><p>To do this, we simply need to adjust <code>begin/value</code> and <code>begin/type</code> to remove the other scope in addition to adding the appropriate one:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Now our nested program runs, and it produces <code>'type-x</code>, which is exactly what we want—the “nearest” scope wins.</p><p>With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.</p><h3><a name="namespaces-that-cross-module-boundaries"></a>Namespaces that cross module boundaries</h3><p>The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.</p><p>Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the <em>other</em> module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export <em>symbols</em>, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named <code>foo</code>, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the <code>require</code> form’s job to attach the correct scopes to imported identifiers.</p><p>This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup> This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary <em>points</em> of having multiple namespaces.</p><p>What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s <code>require</code> and <code>provide</code> forms are extensible, it’s even possible to implement this mangling in a completely invisible way.</p><p>Currently, the scheme that Hackett uses is to prefix <code>#%hackett-type:</code> onto the beginning of any type exports. This can be defined in terms of a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29"><em>provide pre-transformer</em></a>, which is essentially a macro that cooperates with Racket’s <code>provide</code> form to control the export process. In this case, we can define our <code>type-out</code> provide pre-transformer in terms of <a href="https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29"><code>prefix-out</code></a>, a form built-in to Racket that allows prefixing the names of exports:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">type-out</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-provide-pre-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="w"> </span><span class="n">modes</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">pre-expand-export</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">prefix-out</span><span class="w"> </span><span class="n">#%hackett-type:</span> +<span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">type-introducer</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-out</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span> +<span class="w"> </span><span class="n">modes</span><span class="p">)]))))</span></code></pre><p>Note that we call <code>type-introducer</code> in this macro! That’s because we want to ensure that, when a user writes <code>(provide (type-out Foo))</code>, we look for <code>Foo</code> in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that <code>provide</code> knows <em>which</em> <code>Foo</code> is being provided.</p><p>Once we have referenced the correct binding, the use of <code>prefix-out</code> will appropriately add the <code>#%hackett-type:</code> prefix, so the exporting side is already done. Users do need to explicitly write <code>(type-out ....)</code> if they are exporting a particular type-level binding, but this is rarely necessary, since most users use <code>data</code> or <code>class</code> to export datatypes or typeclasses respectively, which can be modified to use <code>type-out</code> internally. Very little user code actually needs to change to support this adjustment.</p><p>Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the <code>#%hackett-type:</code> prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.</p><p>Just as we extended <code>provide</code> with a provide pre-transformer, we can extend <code>require</code> using a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29"><em>require transformer</em></a>. In code, this entire process looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">and~&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">regexp-match</span><span class="w"> </span><span class="sr">#rx"^#%hackett-type:(.+)$"</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="nb">second</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">unmangle-types-in</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-require-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">imports</span><span class="w"> </span><span class="n">sources</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">expand-import</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-in</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">match-lambda</span> +<span class="w"> </span><span class="p">[(</span><span class="k">and</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="n">local-id</span><span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">local-name</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol-&gt;string</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">local-id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">unmangled-type-name</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">local-name</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">unmangled-type-name</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">unmangled-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;symbol</span><span class="w"> </span><span class="n">unmangled-type-name</span><span class="p">)</span> +<span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="n">local-id</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">unmangled-id</span><span class="p">)</span> +<span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="n">i</span><span class="p">))])</span> +<span class="w"> </span><span class="n">imports</span><span class="p">)</span> +<span class="w"> </span><span class="n">sources</span><span class="p">)])))</span></code></pre><p>This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:</p><ol><li><p>It iterates over each import and calls <code>unmangle-type-name</code> on the imported symbol. If the result is <code>#f</code>, that means the import does not have the <code>#%hackett-type:</code> prefix, and it can be safely passed through unchanged.</p></li><li><p>If <code>unmangle-type-name</code> does <em>not</em> return <code>#f</code>, then it returns the unprefixed name, which is then provided to <code>datum-&gt;syntax</code>, which allows users to forge new identifiers in an <em>unhygienic</em> (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from <code>unmangle-type-name</code>, but with the lexical context of the original identifier.</p></li><li><p>Finally, we pass the new identifier to <code>type-introducer</code> to properly add the type scope, injecting the fresh binding into the type namespace.</p></li></ol><p>With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write <code>type-out</code> when exporting types, it is unlikely that users will want to write <code>unmangle-types-in</code> around each and every import in their program. For that reason, we can define a slightly modified version of <code>require</code> that implicitly wraps all of its subforms with <code>unmangle-types-in</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="p">(</span><span class="k">rename-out</span><span class="w"> </span><span class="p">[</span><span class="n">require/unmangle</span><span class="w"> </span><span class="k">require</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">require/unmangle</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-types-in</span><span class="w"> </span><span class="n">require-spec</span><span class="p">)</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>…and we’re done. Now, Hackett modules can properly import and export type-level bindings.</p><h3><a name="namespaces-plus-submodules-the-devil-s-in-the-details"></a>Namespaces plus submodules: the devil’s in the details</h3><p>Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of <em>submodules</em>.</p><p>Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.</p><p>Submodules in Racket come in two flavors: <code>module</code> and <code>module*</code>. The difference is what order, semantically, they are defined in. Submodules defined with <code>module</code> are essentially defined <em>before</em> their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with <code>module*</code> are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.</p><p>How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.</p><p>However, there is <a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29">a special sort of <code>module*</code> submodule that uses <code>#f</code> in place of a module language</a>, which gives a module access to <em>all</em> of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a <code>test</code> submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="c1">; not provided</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span> + +<span class="p">(</span><span class="k">module*</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">rackunit</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">check-equal?</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="mi">41</span><span class="p">)</span><span class="w"> </span><span class="mi">42</span><span class="p">))</span></code></pre><p>It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>Remember that <code>make-syntax-introducer</code> is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module <a href="https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29">instantiation</a>, as ensured by Racket’s <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">separate compilation guarantee</a>. This means that each module gets its <em>own</em> pair of scopes. This means the body of a <code>module*</code> submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.</p><p>Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we <em>can</em> preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29"><code>make-syntax-delta-introducer</code></a>, we can create a syntax introducer the adds or removes the <em>difference</em> between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for <code>value-introducer</code> and <code>type-introducer</code> to always operate on the same scopes on each module instantiation:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-value/type-introducers</span> +<span class="w"> </span><span class="n">value-introducer:id</span><span class="w"> </span><span class="n">type-introducer:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">scopeless-id</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">introducer-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">value-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">type-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">type-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-value/type-introducers</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="n">type-introducer</span><span class="p">)</span></code></pre><p>The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. <em>However</em>, code inside <code>begin-for-syntax</code> blocks is still re-evaluated every time the module is instantiated! This means we are <em>not</em> circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.</p><p>We still use <code>make-syntax-introducer</code> to create our two scopes, but critically, we only call <code>make-syntax-introducer</code> inside the <code>define-value/type-introducers</code> macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds <code>value-id</code> and <code>type-id</code> as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use <code>make-syntax-delta-introducer</code> to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.</p><p>By guaranteeing each namespace’s scope is always the same, even for different modules, <code>module*</code> submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.</p><h3><a name="the-final-stretch-making-scribble-documentation-namespace-aware"></a>The final stretch: making Scribble documentation namespace-aware</h3><p>As discussed in <a href="/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/">my previous blog post</a>, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.</p><p>In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!</p><p>Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.</p><p>To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with <code>t:</code> upon import, and they are bound to Scribble <a href="https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29"><em>element transformers</em></a> that indicate they should be typeset without the <code>t:</code> prefix. Fortunately, Scribble’s documentation forms <em>do</em> understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.</p><p>In practice, this means Hackett documentation must now include a proliferation of <code>t:</code> prefixes. For example, here is the code for a typeset REPL interaction:</p><pre><code class="pygments"><span class="n">@</span><span class="p">(</span><span class="n">hackett-examples</span> +<span class="w"> </span><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">square</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">t:-&gt;</span><span class="w"> </span><span class="n">t:Integer</span><span class="w"> </span><span class="n">t:Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">[[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="p">}])</span> +<span class="w"> </span><span class="p">(</span><span class="n">square</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span></code></pre><p>Note the use of <code>t:-&gt;</code> and <code>t:Integer</code> instead of <code>-&gt;</code> and <code>Integer</code>. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.</p><p>This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides <code>deftype</code> and <code>deftycon</code> forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to <code>t:</code>-prefixed identifiers to properly index documented forms. Similarly, <code>defdata</code> and <code>defclass</code> have been updated with an understanding of types.</p><p>The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of <code>defform</code> slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into <code>t:</code>-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.</p><h2><a name="analysis-and-unsolved-problems"></a>Analysis and unsolved problems</h2><p>When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).</p><p>For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom <code>require</code> form, types provided by a module’s <em>language</em> are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike <code>require</code>’s require transformers.</p><p>To circumvent this restriction, <code>#lang hackett</code>’s reader includes a somewhat ad-hoc solution that actually inserts a <code>require</code> into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.</p><p>That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do <em>any</em> special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully <a href="http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html">internalizes extra-linguistic mechanisms</a>.</p><p>As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:</p><ul><li><p><strong>Multi-parameter typeclasses</strong> are implemented, along with <strong>default typeclass method implementations</strong>.</p></li><li><p>Pattern-matching performs basic <strong>exhaustiveness checking</strong>, so unmatched cases are a compile-time error.</p></li><li><p>Hackett ships with a <strong>larger standard library</strong>, including an <code>Either</code> type and appropriate functions, an <code>Identity</code> type, a <code>MonadTrans</code> typeclass, and the <code>ReaderT</code> and <code>ErrorT</code> monad transformers.</p></li><li><p><strong>More things are documented</strong>, and parts of the documentation are slightly improved. Additionally, <strong>Hackett’s internals are much more heavily commented</strong>, hopefully making the project more accessible to new contributors.</p></li><li><p><strong>Parts of the typechecker are dramatically simplified</strong>, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.</p></li><li><p>As always, various bug fixes.</p></li></ul><p>Finally, special mention to two new contributors to Hackett, <a href="https://github.com/iitalics">Milo Turner</a> and <a href="https://github.com/Shamrock-Frost">Brendan Murphy</a>. Also special thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> and <a href="https://github.com/michaelballantyne">Michael Ballantyne</a> for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.</p><ol class="footnotes"><li id="footnote-1"><p>“But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have <code>#lang</code>. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate <code>#lang</code>, not a modified version of Hackett, so Hackett can optimize its user experience for what it <em>is</em>, not what it might be someday. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow <code>*</code> to be used to mean “type” at the kind level and “multiply” at the value level. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, <em>per phase</em>, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Hackett progress report: documentation, quality of life, and snakehttps://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/https://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/28 Aug 2017<article><p>Three months ago, <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">I wrote a blog post describing my new, prototype implementation of my programming language, Hackett</a>. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/"><strong>the first (albeit quite incomplete) approach to Hackett’s documentation</strong></a>.</p><p>I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.</p><h2><a name="a-philosophy-of-documentation"></a>A philosophy of documentation</h2><p>Racket, as a project, has always had <a href="http://docs.racket-lang.org">wonderful documentation</a>. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably <a href="http://docs.racket-lang.org/scribble/index.html">Scribble, the Racket documentation tool</a>. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with <a href="http://docs.racket-lang.org/scribble/plt-manuals.html">a powerful library for documenting Racket code</a>. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.</p><p>Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an <code>@</code> character instead of <code>\</code>. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use <a href="http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29">the provided <code>defproc</code> form</a>:</p><pre><code class="pygments"><span class="n">@defproc</span><span class="p">[(</span><span class="nb">add1</span><span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="nb">number?</span><span class="p">])</span><span class="w"> </span><span class="nb">number?</span><span class="p">]{</span> +<span class="n">Returns</span><span class="w"> </span><span class="n">@racket</span><span class="p">[(</span><span class="nb">+</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="mi">1</span><span class="p">)]</span><span class="o">.</span><span class="p">}</span></code></pre><p>This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into <a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29">something like this</a>:</p><p><a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29"></a></p><p>The fact that Scribble documents are fully-fledged <em>programs</em> equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is <a href="http://docs.racket-lang.org/scribble/eval.html">the <code>scribble/example</code> module</a>, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special <code>eval:check</code> form that enables <a href="https://docs.python.org/3/library/doctest.html">doctest</a>-like checking, which allows documentation to serve double duty as a test suite.</p><p>Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is <em>designed</em> with the goal of supporting many different programming languages, and Scribble is no exception. Things like <a href="http://docs.racket-lang.org/scribble/eval.html"><code>scribble/example</code></a> essentially work out of the box with Hackett, and most of <a href="http://docs.racket-lang.org/scribble/plt-manuals.html"><code>scribble/manual</code></a> can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The <code>defproc</code> and <code>defstruct</code> forms are hardly builtins; they are defined as part of the <code>scribble/manual</code> library in terms of Scribble primitives, and <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt">we can do the same</a>.</p><p>Hackett’s documentation already defines three new forms, <code>defdata</code>, <code>defclass</code>, and <code>defmethod</code>, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the <code>Functor</code> typeclass using <code>defclass</code> like this:</p><pre><code class="pygments"><span class="n">@defclass</span><span class="p">[(</span><span class="n">Functor</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="nb">map</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="p">)})]]{</span> + +<span class="n">A</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">@deftech</span><span class="p">{</span><span class="n">functors</span><span class="p">}</span><span class="o">,</span><span class="w"> </span><span class="n">essentially</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="k">provide</span><span class="w"> </span><span class="n">a</span> +<span class="n">mapping</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">“piercing”</span><span class="w"> </span><span class="n">operation.</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">@racket</span><span class="p">[</span><span class="nb">map</span><span class="p">]</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">viewed</span><span class="w"> </span><span class="n">in</span> +<span class="n">different</span><span class="w"> </span><span class="n">ways:</span> + +<span class="k">...</span><span class="p">}</span></code></pre><p>With only a little more than the above code, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29">Hackett’s documentation includes a beautifully-typeset definition of the <code>Functor</code> typeclass</a>, including examples and rich prose:</p><p><a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29"></a></p><p>Scribble makes Hackett’s documentation shine.</p><h3><a name="a-tale-of-two-users"></a>A tale of two users</h3><p>For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.</p><p>A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.</p><p>However, for experienced users, an authoritative reference is <em>exactly</em> what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.</p><p>This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:</p><ul><li><p>Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.</p><p><a href="https://docs.oracle.com/javase/8/docs/api/">Java’s API documentation</a> documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly <em>small</em> language that does not often change.</p><p>On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java <em>does</em> provide guide-style documentation in the form of the <a href="https://docs.oracle.com/javase/tutorial/">Java Tutorials</a>, but they are of inconsistent quality.</p><p>More importantly, while the Java tutorials link to the API docs, the reverse is <strong>not</strong> true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.</p><p>Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.</p></li><li><p>Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.</p><p>The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is <a href="https://developer.mozilla.org/en-US/">MDN</a>. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.</p><p>The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.</p><p>This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. <a href="http://ramdajs.com/docs/">Ramda’s documentation is nothing but a reference</a>, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, <a href="http://passportjs.org/docs">Passport’s docs are essentially <em>only</em> a set of tutorials</a>, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method <em>does</em>. Fortunately, <a href="https://facebook.github.io/react/docs/hello-world.html">there are some libraries, like React</a>, that absolutely <em>nail</em> this, and they have both styles of documentation that are <strong>actually cross-referenced</strong>. Unfortunately, those are mostly the exceptions, not the norm.</p></li><li><p><a href="https://docs.python.org/3/index.html">Python’s documentation is interesting</a>, since it includes a set of tutorials alongside the API reference, and it <em>also</em> ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on <code>if</code> links to the section in the reference about <code>if</code>, but nothing goes in the other direction, which is something of a missed opportunity.</p></li><li><p><a href="https://hackage.haskell.org/package/base">Haskell manages to be especially bad here</a> (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.</p><p>It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and <a href="https://www.haskell.org/haddock/">Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look</a>.</p></li></ul><p>The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with <em>two</em> pieces of documentation: the <a href="http://docs.racket-lang.org/guide/index.html">Racket Guide</a> and the <a href="http://docs.racket-lang.org/reference/index.html">Racket Reference</a>. The guide includes over <strong>one hundred thousand</strong> words of explanations and examples, and the reference includes roughly <strong>half a million</strong>. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.</p><p>Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the <a href="http://docs.racket-lang.org/hackett/guide.html">Hackett Guide</a> and the <a href="http://docs.racket-lang.org/hackett/reference.html">Hackett Reference</a>, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily <em>need</em> to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a <em>lot</em> more accessible without making it any less useful for power users.</p><h2><a name="rounding-hackett-s-library-sanding-its-edges"></a>Rounding Hackett’s library, sanding its edges</h2><p>One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a <em>lot</em> of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.</p><p>Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:</p><ul><li><p>Hackett includes a <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29"><code>Double</code></a> type for working with IEEE 754 double-precision floating-point numbers.</p></li><li><p>Local definitions are supported via the <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29"><code>let</code></a> and <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29"><code>letrec</code></a> forms.</p></li><li><p>The prelude includes many more functions, especially <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29">functions on lists</a>.</p></li><li><p>The Hackett reader has been adjusted to support using <code>.</code> as a bare symbol, since <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29"><code>.</code> is the function composition operator</a>.</p></li><li><p>The Hackett REPL supports many more forms, including <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29">ADT</a>, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29">class</a>, and <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29">instance</a> definitions. Additionally, the REPL now uses <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a> instances to display the results of expressions. To compensate for the inability to print non-<a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a>able things, a new <code>(#:type expr)</code> syntax is permitted to print the type of <em>any</em> expression.</p></li><li><p>Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.</p></li></ul><p>Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189">heavily commented</a> with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.</p><p>One of the most exciting things about documenting Hackett has been realizing just <em>how much</em> already exists. Seriously, if you have gotten to this point in the blog post but haven’t read <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/">the actual documentation</a> yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for <em>me</em>: it’s a language I’d like to use.</p><p>Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!</p><h2><a name="a-demo-with-pictures"></a>A demo with pictures</h2><p>Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?</p><p>Hackett ships with a special package of demo libraries in the aptly-named <code>hackett-demo</code> package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">the previous Hackett blog post</a>, I demonstrated the capabilities of <code>hackett/demo/web-server</code>. In this blog post, we’re going to use <code>hackett/demo/pict</code> and <code>hackett/demo/pict/universe</code>, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!</p><p>As always, we’ll start with <code>#lang hackett</code>, and we’ll import the necessary libraries:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/pict</span> +<span class="w"> </span><span class="n">hackett/demo/pict/universe</span><span class="p">)</span></code></pre><p>With that, we can start immediately with a tiny example. Just to see how <code>hackett/demo/pict</code> works, let’s start by rendering a red square. We can do this by writing a <code>main</code> action that calls <code>print-pict</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))))</span></code></pre><p>If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!</p><p></p><p>Using the REPL, we can inspect the type of <code>print-pict</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">print-pict</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Unit</span><span class="p">))</span></code></pre><p>Unsurprisingly, displaying a picture to the screen needs <code>IO</code>. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of <code>filled-square</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">filled-square</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">Pict</span><span class="p">)</span></code></pre><p>No <code>IO</code> to be seen! This is because “picts” are entirely <em>pure</em> values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">{(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))</span> +<span class="w"> </span><span class="n">hc-append</span> +<span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))}))</span></code></pre><p>This code will print out a red square to the left of a blue one.</p><p></p><p>Again, <code>hc-append</code> is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">hc-append</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="n">Pict</span><span class="p">))</span></code></pre><p>Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!</p><h3><a name="implementing-a-snake-clone"></a>Implementing a snake clone</h3><p>This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the <code>pict</code> or <code>universe</code> libraries. Hopefully it’s still illustrative.</p><p>We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an <code>Eq</code> instance for our points.</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="n">d:left</span><span class="w"> </span><span class="n">d:right</span><span class="w"> </span><span class="n">d:up</span><span class="w"> </span><span class="n">d:down</span><span class="p">)</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">Eq</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="k">==</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)]</span><span class="w"> </span><span class="p">{{</span><span class="n">a</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">c</span><span class="p">}</span><span class="w"> </span><span class="n">&amp;&amp;</span><span class="w"> </span><span class="p">{</span><span class="n">b</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">d</span><span class="p">}})])</span></code></pre><p>With these two datatypes, we can implement a <code>move</code> function that accepts a point and a direction and produces a new point for an adjacent tile:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Direction</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:left</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:right</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:up</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">})]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:down</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">})])</span></code></pre><p>The next step is to define a type for our world state. The <code>big-bang</code> library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span> +<span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="c1">; snake direction</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; snake blocks</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; food blocks</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">set-ws-direction</span><span class="w"> </span><span class="p">[[</span><span class="n">d</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)])</span></code></pre><p>Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-width</span><span class="w"> </span><span class="mi">50</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-height</span><span class="w"> </span><span class="mi">30</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="p">{(</span><span class="n">d*</span><span class="w"> </span><span class="mf">15.0</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">integer-&gt;double</span><span class="p">})</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="p">(</span><span class="n">blank-rect</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-height</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">13.0</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="n">block</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="n">block</span><span class="p">))</span></code></pre><p>Now we can write our actual <code>render</code> function. To do this, we simply need to render each <code>Point</code> in our <code>World-State</code>’s two lists as a block on an <code>empty-board</code>. We’ll write a helper function, <code>render-on-board</code>, which does exactly that:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render-on-board</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Pict</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">pict</span><span class="w"> </span><span class="n">points</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">foldr</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">acc</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">pict</span><span class="p">))</span> +<span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="n">points</span><span class="p">)])</span></code></pre><p>This function uses <code>foldr</code> to collect each point and place the provided pict at the right location using <code>pin-over</code> on an empty board. Using <code>render-on-board</code>, we can write the <code>render</code> function in just a couple of lines:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)</span> +<span class="w"> </span><span class="mf">0.0</span><span class="w"> </span><span class="mf">0.0</span> +<span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="n">food-points</span><span class="p">))])</span></code></pre><p>Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the <code>big-bang</code> library provides a <code>random-integer</code> function, which we can use to write a <code>random-point</code> action:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">random-point</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">point</span><span class="w"> </span><span class="n">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span> +<span class="w"> </span><span class="n">&lt;*&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-height</span><span class="p">)})</span></code></pre><p>Hackett supports applicative notation using infix operators, so <code>random-point</code> looks remarkably readable. It also runs in <code>IO</code>, since the result is, obviously, random. Fortunately, the <code>on-tick</code> function runs in <code>IO</code> as well (unlike <code>render</code>, which must be completely pure), so we can use <code>random-point</code> when necessary to generate a new food block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">init!</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)})</span> +<span class="w"> </span><span class="p">{</span><span class="nb">reverse</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tail!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="nb">reverse</span><span class="p">})</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">new-snake-point</span><span class="w"> </span><span class="p">(</span><span class="n">move</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">(</span><span class="n">head!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">elem?</span><span class="w"> </span><span class="n">food-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">random-point</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">snake-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">delete</span><span class="w"> </span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">food-points</span><span class="p">)})))</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">init!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)}</span> +<span class="w"> </span><span class="n">food-points</span><span class="p">))))])</span></code></pre><p>This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to <code>new-snake-point</code>, then checks if there is a food block at that location. If there is, it generates a <code>new-food-point</code>, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.</p><p>The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our <code>set-ws-direction</code> function that we wrote earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-key</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">KeyEvent</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:left</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:left</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:right</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:right</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:up</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:up</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:down</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:down</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="k">_</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id</span><span class="p">}])</span></code></pre><p>The <code>on-key</code> function runs in <code>IO</code>, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in <code>pure</code>.</p><p>We’re almost done now—all we need to do is set up the <em>initial</em> state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">initial-state</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">initial-food</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">sequence</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="n">repeat</span><span class="w"> </span><span class="n">random-point</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d:right</span> +<span class="w"> </span><span class="p">{(</span><span class="n">point</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">24</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">23</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">nil</span><span class="p">}</span> +<span class="w"> </span><span class="n">initial-food</span><span class="p">))))</span></code></pre><p>Notably, we can use the <code>repeat</code> function to create an infinite list of <code>random-point</code> actions, <code>take</code> the first five of them, then call <code>sequence</code> to execute them from left to right. Now, all we have to do is put the pieces together in a <code>main</code> block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">state</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">initial-state</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">big-bang</span><span class="w"> </span><span class="n">state</span> +<span class="w"> </span><span class="kd">#:to-draw</span><span class="w"> </span><span class="n">render</span> +<span class="w"> </span><span class="kd">#:on-tick</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="mf">0.2</span> +<span class="w"> </span><span class="kd">#:on-key</span><span class="w"> </span><span class="n">on-key</span><span class="p">)))</span></code></pre><p>And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.</p><p></p><h2><a name="contributing-to-hackett"></a>Contributing to Hackett</h2><p>If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.</p><p>On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, <a href="https://github.com/lexi-lambda/hackett/issues">Hackett has an issue tracker</a>, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.</p><p>Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to <a href="https://github.com/lexi-lambda/hackett/issues">open an issue on GitHub</a>, send me a message on the <code>#racket</code> IRC channel on Freenode, or ping me on <a href="http://racket-slack.herokuapp.com">the Racket Slack team</a>.</p><h2><a name="acknowledgements"></a>Acknowledgements</h2><p>Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to <a href="https://github.com/gelisam">Samuel Gélineau, aka gelisam</a>, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.</p><p>Additionally, I want to specially thank <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.</p><p>Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.</p><ol class="footnotes"></ol></article>User-programmable infix operators in Rackethttps://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/https://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/12 Aug 2017<article><p>Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in <a href="https://github.com/lexi-lambda/hackett">Hackett</a>, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.</p><p>Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done <em>without</em> modifying the stock <code>#lang racket</code> reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.</p><h2><a name="our-mission"></a>Our mission</h2><p>Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we <em>do</em> want:</p><ul><li><p>Infix operators should be user-extensible, not limited to a special set of built-in operators.</p></li><li><p>Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.</p></li><li><p>We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like <code>cons</code>. This allows <code>5 - 1 - 2</code> to be parsed as <code>(- (- 5 1) 2)</code>, but <code>5 :: 1 :: nil</code> to be parsed as <code>(:: 5 (:: 1 nil))</code>.</p></li></ul><p>These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:</p><ul><li><p>We will <strong>not</strong> permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing <code>(+ 1 2)</code> with <code>{1 + 2}</code>.</p></li><li><p>Our implementation will <strong>not</strong> support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.</p></li><li><p>All operators will be binary, and we will <strong>not</strong> support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.</p></li></ul><p>With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"infix.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> + +<span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">{</span><span class="mi">10</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="c1">; =&gt; &#39;(1 7)</span></code></pre><p>Let’s get started.</p><h2><a name="implementing-infix-operators"></a>Implementing infix operators</h2><p>Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:</p><ol><li><p>How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?</p></li><li><p>How can we associate fixity information with certain operators?</p></li></ol><p>We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.</p><p>So, how <em>do</em> we detect if a Racket expression is surrounded by curly braces? Normally, in <code>#lang racket</code>, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated <em>exactly</em> the same as parentheses:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.</p><p>Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29"><em>syntax objects</em></a>. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as <a href="http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29"><em>syntax properties</em></a>. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s <a href="http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29"><em>reader</em></a> (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named <a href="http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29"><code>'paren-shape</code></a>.</p><p>This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="no">#f</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\[</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\{</span></code></pre><p>This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.</p><h3><a name="customizing-application"></a>Customizing application</h3><p>Racket is a language <em>designed</em> to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named <a href="http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29"><code>#%app</code></a>, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>…into this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>What’s special about <code>#%app</code> is that the macroexpander will use whichever <code>#%app</code> is in scope in the expression’s lexical context, so if we write our own version of <code>#%app</code>, it will be used instead of the one from <code>#lang racket</code>. This is what we will use to hook into ordinary Racket expressions.</p><p>To write our custom version of <code>#%app</code>, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the <code>'paren-shape</code> syntax property, <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29"><code>syntax/parse/class/paren-shape</code></a>. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>This code will transform any applications surrounded in curly braces into one that starts with <code>#%infix</code> instead of <code>#%app</code>, so <code>{1 + 2}</code> will become <code>(#%infix 1 + 2)</code>, for example. The identifier <code>#%infix</code> isn’t actually special in any way, it just has a funny name, but we haven’t actually defined <code>#%infix</code> yet, so we need to do that next!</p><p>To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like <code>{1 + 2}</code>, should be converted into the equivalent prefix expressions, in this case <code>(+ 1 2)</code>. We can do this with a simple macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)])</span></code></pre><p>Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the <code>#%app</code> from <code>racket/base</code>, which will avoid any accidental infinite recursion between our <code>#%app</code> and <code>#%infix</code>. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>That’s pretty cool!</p><p>Of course, we probably want to support infix applications with more than just a single binary operator, such as <code>{1 + 2 + 3}</code>. We can implement that just by adding another case to <code>#%infix</code> that handles more subforms:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">10</span></code></pre><p>I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">5</span></code></pre><p>Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as <code>cons</code>, nested <code>-&gt;</code> types or contracts for curried functions, and <code>expt</code>, the exponentiation operator.</p><h3><a name="tracking-operator-fixity"></a>Tracking operator fixity</h3><p>Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.</p><p>Essentially, Racket (like Scheme) uses a <code>define-syntax</code> form to define macros, which is what <code>define-syntax-parser</code> eventually expands into. However, unlike Scheme, Racket’s <code>define-syntax</code> is not <em>just</em> for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Once a binding has been defined using <code>define-syntax</code>, a macro can look up the value associated with it by using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, which returns the compile-time value associated with an identifier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">foo</span><span class="p">)))</span> +<span class="c1">; =&gt; 3</span></code></pre><p>The cool thing is that <code>syntax-local-value</code> gets the value associated with a specific <em>binding</em>, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use <code>syntax-local-value</code> to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the <code>foo</code> binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because <code>foo</code> is not bound to anything at runtime.</p><p>To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the <a href="http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29"><code>prop:procedure</code></a> structure type property.</p><p>If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">))))</span></code></pre><p>This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for <code>runtime-binding</code>, but we can also include a value of our choosing for <code>fixity</code>. Here’s an example:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="nb">cons</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="p">))</span></code></pre><p>This new <code>::</code> binding will act, in every way, just like <code>cons</code>. If we use it in the REPL, you can see that it acts exactly the same:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></code></pre><p>However, we can also use <code>syntax-local-value</code> to extract this binding’s fixity at compile-time, and that’s what makes it interesting:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">::</span><span class="p">))))</span> +<span class="c1">; =&gt; &#39;right</span></code></pre><p>Using this extra compile-time information, we can adjust our <code>#%infix</code> macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used <code>syntax/parse/class/paren-shape</code> to make decisions based on the <code>'paren-shape</code> syntax property, we can use <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29"><code>syntax/parse/class/local-value</code></a> to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span></code></pre><p>Now, we can update <code>#%infix</code> to use our new <code>infix-op</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span></code></pre><p>Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via <code>#:when</code> clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of <code>#%infix</code>, we can successfully use <code>::</code> in an infix expression, and it will be parsed with the associativity that we expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Exciting!</p><h3><a name="a-nicer-interface-for-defining-infix-operators"></a>A nicer interface for defining infix operators</h3><p>We currently have to define infix operators by explicitly using <code>define-syntax</code>, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the <code>define-infix-operator</code> form from the example at the very beginning of this blog post.</p><p>Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the <code>define-syntax</code> definitions we were already writing:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span></code></pre><p>With this in hand, we can define some infix operators with a much nicer syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary <code>#lang racket</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">-1</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">256</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">64</span></code></pre><p>And you know what’s most amazing about this? The entire thing is <strong>only 50 lines of code</strong>. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/local-value</span> +<span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span> +<span class="w"> </span><span class="n">syntax/transformer</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>Racket is a hell of a programming language.</p><h2><a name="applications-limitations-and-implications"></a>Applications, limitations, and implications</h2><p>This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am <em>almost certain</em> that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even <em>locally</em>.</p><p>What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as <code>let-syntax</code> and <code>syntax-parameterize</code>. Using these tools, it would be entirely possible to implement a <code>with-fixity</code> macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make <code>/</code> right associative within a block of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="m">1/6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="nb">/</span><span class="w"> </span><span class="n">right</span><span class="p">])</span> +<span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span> +<span class="mi">1</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of <code>splicing-let</code> and <code>splicing-let-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span> +<span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="n">op:id</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}}]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">op-tmp</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">op</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let</span><span class="w"> </span><span class="p">([</span><span class="n">op-tmp</span><span class="w"> </span><span class="n">op</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">op-tmp</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span></code></pre><p>This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations <em>between</em> operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.</p><p>As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in <a href="https://github.com/lexi-lambda/hackett">Hackett</a> to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow <em>any</em> expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.</p><p>If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt">this file for the definition of infix bindings</a>, as well as <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101">this file for the defintion of infix application</a>. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like <code>{_ - 1}</code> to serve as a shorthand for <code>(lambda [x] {x - 1})</code>, but I haven’t yet decided if I like the tradeoffs involved.</p><p>It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in <code>#lang racket</code> without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.</p><p>Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to <code>#lang hackett</code>, there’s no reason something similar but more powerful couldn’t be built as a separate library by a <em>user</em> of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.</p><ol class="footnotes"></ol></article>Unit testing effectful Haskell with monad-mockhttps://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/https://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/29 Jun 2017<article><p>Nearly eight months ago, <a href="/blog/2016/10/03/using-types-to-unit-test-in-haskell/">I wrote a blog post about unit testing effectful Haskell code</a> using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, <a href="https://hackage.haskell.org/package/monad-mock">monad-mock</a>.</p><h2><a name="a-first-glance-at-monad-mock"></a>A first glance at monad-mock</h2><p>The monad-mock library is, first and foremost, designed to be <em>easy</em>. It doesn’t ask much from you, and it requires almost zero boilerplate.</p><p>The first step is to write an mtl-style interface that encodes an effect you want to mock. For example, you might want to test some code that interacts with the filesystem:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Now you just have to write your code as normal. For demonstration purposes, here’s a function that defines copying a file in terms of <code>readFile</code> and <code>writeFile</code>:</p><pre><code class="pygments"><span class="nf">copyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>Making this function work on the real filesystem is trivial, since we just need to define an instance of <code>MonadFileSystem</code> for <code>IO</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span></code></pre><p>But how do we test this? Well, we <em>could</em> run some real code in <code>IO</code>, which might not be so bad for such a simple function, but this seems like a bad idea. For one thing, a bad implementation of <code>copyFile</code> could do some pretty horrible things if it misbehaved and decided to overwrite important files, and if you’re constantly running a test suite whenever a file changes, it’s easy to imagine causing a lot of damage. Running tests against the real filesystem also makes tests slower and harder to parallelize, and it only gets much worse once you are doing more complex effects than interacting with the filesystem.</p><p>Using monad-mock, we can test this function in just a couple of lines of code:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Exception</span><span class="w"> </span><span class="p">(</span><span class="nf">evaluate</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock.TH</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Data.Function</span><span class="w"> </span><span class="p">((</span><span class="o">&amp;</span><span class="p">))</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Test.Hspec</span> + +<span class="nf">makeMock</span><span class="w"> </span><span class="s">"FileSystemAction"</span><span class="w"> </span><span class="p">[</span><span class="n">ts</span><span class="o">|</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="o">|</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"copyFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reads a file and writes its contents to another file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span></code></pre><p>That’s it!</p><p>The last two lines of the above snippet are the real interesting bits, which specify the actions that are expected to be executed, and it couples them with their results. You will find that if you tweak the list in any way, such as reordering the actions, eliminating one or both of them, or adding an additional action to the end, the test will fail. We could even turn this into a property-based test that generated arbitrary file paths and file contents.</p><p>Admittedly, in this trivial example, the mock is a little silly, since converting this into a property-based test would demonstrate how much we’ve basically just reimplemented the function in our test. However, once our function starts to do somewhat more complicated things, then our tests become more meaningful. Here’s a similar function that only copies a file if it is nonempty:</p><pre><code class="pygments"><span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This function has some logic which is very clearly <em>not</em> expressed in its type, and it would be difficult to encode that information into the type in a safe way. Fortunately, we can guarantee that it works by writing some tests:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">""</span><span class="w"> </span><span class="p">]</span></code></pre><p>These tests are much more useful, and they have some actual value to them. Imagine we had accidentally written <code>when</code> instead of <code>unless</code>, an easy typo to make. Our tests would fail with some useful error messages:</p><pre><code>1) copyNonemptyFile copies a file with contents + uncaught exception: runMockT: expected the following unexecuted actions to be run: + WriteFile "bar.txt" "contents" + +2) copyNonemptyFile does nothing with an empty file + uncaught exception: runMockT: expected end of program, called writeFile + given action: WriteFile "bar.txt" "" +</code></pre><p>You now know enough to write tests with monad-mock.</p><h2><a name="why-unit-test"></a>Why unit test?</h2><p>When the issue of testing is brought up in Haskell, it is often treated with a certain distaste by a portion of the community. There are some points I’ve seen a number of times, and though they take different forms, they boil down to two ideas:</p><ol><li><p>“Haskell code does not need tests because the type system can prove correctness.”</p></li><li><p>“Testing in Haskell is trivial because it is a pure language, and testing pure functions is easy.”</p></li></ol><p>I’ve been writing Haskell professionally for over a year now, and I can happily say that there <em>is</em> some truth to both of those things! When my Haskell code typechecks, I feel a confidence in it that I would not feel were I using a language with a less powerful type system. Furthermore, Haskell encourages a “pure core, impure shell” approach to system design that makes testing many things pleasant and straightforward, and it completely eliminates the worry of subtle nondeterminism leaking into tests.</p><p>That said, Haskell is not a proof assistant, and its type system cannot guarantee everything, especially for code that operates on the boundaries of what Haskell can control. For much the same reason, I find that my pure code is the code I am <em>least</em> likely to need to test, since it is also the code with the strongest type safety guarantees, operating on types in my application’s domain. In contrast, the effectful code is often what I find the most value in extensively testing, since it often contains the most subtle complexity, and it is frequently difficult or even impossible to encode into types.</p><p>Haskell has the power to provide remarkably strong correctness guarantees with a surprisingly small amount of effort by using a combination of tests and types, using each to accommodate for the other’s weaknesses and playing to each technique’s strengths. Some code is test-driven, other code is type-driven. Most code ends up being a mix of both. Testing is just a tool like any other, and it’s nice to feel confident in one’s ability to effectively structure code in a decoupled, testable manner.</p><h2><a name="why-mock"></a>Why mock?</h2><p>Even if you accept that testing is good, the question of whether or not to <em>mock</em> is a subtler issue. To some people, “unit testing” is synonymous with mocks. This is emphatically not true, and in fact, overly aggressive mocking is one of the best ways to make your test suite completely worthless. The monad-mock approach to mocking is a bit more principled than mocking in many dynamic, object-oriented languages, but it comes with many of the same drawbacks: mocks couple your tests to your implementation in ways that make them less valuable and less meaningful.</p><p>For the <code>MonadFileSystem</code> example above, I would actually probably <em>not</em> use a mock. Instead, I would use a <strong>fake</strong>, in-memory filesystem implementation:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)])</span> +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">second</span><span class="w"> </span><span class="n">sort</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">runStateT</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">fs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="o">&amp;</span> +<span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"readFile: no such file ‘"</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"’"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">filter</span><span class="w"> </span><span class="p">((</span><span class="o">/=</span><span class="w"> </span><span class="n">path</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fst</span><span class="p">)</span><span class="w"> </span><span class="n">fs</span></code></pre><p>The above snippet demonstrates how easy it is to define a <code>MonadFileSystem</code> implementation in terms of <code>StateT</code>, and while this may seem like a lot of boilerplate, it really isn’t. You have to write a fake <em>once</em> per interface, and the above block is a minuscule twelve lines of code. With this technique, you are still able to write tests that depend on the state of the filesystem before and after running the implementation, but you decouple yourself from the precise process of getting there:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"bar.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">),</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is better than using a mock, and I would highly recommend doing it if you can! However, a lot of real applications have to interact with services of much greater complexity than an idealized filesystem, and creating that sort of in-memory fake is not always practical. One such situation might be interacting with AWS CloudFormation, for example:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">createStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackName</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StackTemplate</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackId</span><span class="p">)</span> +<span class="w"> </span><span class="n">listStacks</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="p">[</span><span class="kt">StackSummaries</span><span class="p">])</span> +<span class="w"> </span><span class="n">describeStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackId</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackInfo</span><span class="p">)</span> +<span class="w"> </span><span class="c1">-- and so on...</span></code></pre><p>AWS is a very complex system, and it can do dozens of different things (and fail in dozens of different ways) based on an equally complex set of inputs. For example, in the above API, <code>createStack</code> needs to parse its template, which can be YAML or JSON, in order to determine which of many possible errors and behaviors can be produced, both on the initial call and on subsequent ones.</p><p>Creating a fake implementation of <em>AWS</em> is hardly feasible, and this is where a mock can be useful. By simply writing <code>makeMock "AWSAction" [ts| MonadAWS |]</code>, we can test functions that interact with AWS in a pure way without necessarily needing to replicate all of its complexity.</p><h3><a name="isolating-mocks"></a>Isolating mocks</h3><p>Of course, tests that use mocks provide less value than tests that use “smarter” fakes, since they are far more tightly coupled to the implementation, and it’s dramatically more likely that you will need to change the tests when you change the logic. To avoid this, it can be helpful to create multiple interfaces to the same thing: a high-level interface and a low-level one. If our above <code>MonadAWS</code> is a low-level interface, we could create a high-level counterpart that does precisely what our application needs:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDeploy</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">executeDeployment</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>When running our application “for real”, we would use <code>MonadAWS</code> to implement <code>MonadDeploy</code>:</p><pre><code class="pygments"><span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>The nice thing about this is we can actually test <code>executeDeploymentImpl</code> using a <code>MonadAWS</code> mock, so we can still have unit test coverage of the code on the boundaries of our system! Additionally, by containing the mock to a single place, we can test the rest of our code using a smarter fake implementation of <code>MonadDeploy</code>, helping to decouple our code from AWS’s complex API and improve the reliability and usefulness of our test suite.</p><p>They key point here is that mocking is just a small piece of the larger testing puzzle in <em>any</em> language, and that is just as true in Haskell. An overemphasis on mocking is an easy way to end up with a test suite that feels useless, probably because it is. Use mocks as a technique to insulate your application from the complexity in others’ APIs, then use more domain-specific testing techniques and type-level assertions to ensure the correctness of your logic.</p><h2><a name="how-monad-mock-works"></a>How monad-mock works</h2><p>If you’ve read this far and are convinced that monad-mock is useful, you may safely stop reading now. However, if you are interested in the details of what it actually does and what makes it tick, the rest of this blog post is going to focus on how the implementation works and how it compares to other techniques.</p><p>The centerpiece of monad-mock’s API is its monad transformer, <code>MockT</code>, which is a type constructor that accepts three types:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="p">)</span></code></pre><p>The <code>m</code> and <code>a</code> type variables obviously correspond to the usual monad transformer arguments, which represent the underlying monad and the result of the monadic computation, respectively. The <code>f</code> variable is more interesting, since it’s what makes <code>MockT</code> work at all, and it isn’t even a type: it’s a type constructor with kind <code>* -&gt; *</code>. What does it mean?</p><p>Looking at the type signature of <code>runMockT</code> gives us a little bit more information about what that <code>f</code> actually represents:</p><pre><code class="pygments"><span class="nf">runMockT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>This type signature provides two pieces of key information:</p><ol><li><p>The <code>f</code> parameter is constrained by the <code>Action f</code> constraint.</p></li><li><p>Running a mocked computation requires supplying a list of <code>WithResult f</code> values. This list corresponds to the list of expectations provided to <code>runMock</code> in earlier examples.</p></li></ol><p>To understand both of these things, it helps to examine the definition of an actual datatype that can have an <code>Action</code> instance. For the filesystem example, the action datatype looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Notice how each constructor clearly corresponds to one of the methods of <code>MonadFileSystem</code>, with a type to match. Now the purpose of the type provided to the <code>FileSystemAction</code> constructor (in this case <code>r</code>) should hopefully become clear: it represents the type of the value <em>produced</em> by each method. Also note that the type is completely phantom—it does not appear in negative position in any of the constructors.</p><p>With this in mind, we can take a look at the definition of <code>WithResult</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="p">(</span><span class="kt">:-&gt;</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span></code></pre><p>This is what defines the <code>(:-&gt;)</code> constructor from earlier in the blog post, and you can see that it effectively just represents a tuple of an action and a value of its associated result. It’s completely type-safe, since it ensures the result matches the type argument to the action.</p><p>Finally, this brings us to the <code>Action</code> class, which is not complex, but is unfortunately necessary:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">eqAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="n">showAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Notice that these methods are effectively just <code>(==)</code> and <code>show</code>, lifted to type constructors of kind <code>* -&gt; *</code>. One significant difference is that <code>eqAction</code> produces <code>Maybe (a :~: b)</code> instead of <code>Bool</code>, where <code>(:~:)</code> is from <code>Data.Type.Equality</code>. This is a type equality witness, which means a successful equality between two values allows the compiler to be sure that the two <em>types</em> are equal. This is necessary for the implementation of <code>runMockT</code> due to the phantom type in actions—in order to convince GHC that we can properly return the result of a mocked action, we need to assure it that the value we’re going to return is actually of the proper type.</p><p>Implementing this typeclass is not particularly burdensome, but it’s entirely boilerplate, so even if you want to define your own action type (that is, you don’t want to use <code>makeMock</code>), you can use the <code>deriveAction</code> function from <code>Control.Monad.Mock.TH</code> to derive an <code>Action</code> instance on an existing datatype.</p><h3><a name="connecting-the-mock-to-its-class"></a>Connecting the mock to its class</h3><p>Now that we have an action with which to mock a class, we need to actually define an instance of that class for <code>MockT</code>. For this process, monad-mock provides a <code>mockAction</code> function with the following type:</p><pre><code class="pygments"><span class="nf">mockAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span></code></pre><p>This function accepts two arguments: the name of the method being mocked and the action that represents the current call. This is easier to illustrate with an actual instance of <code>MonadFileSystem</code> using <code>MockT</code> and our <code>FileSystemAction</code> type:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MockT</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"readFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"writeFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>This allows <code>readFile</code> and <code>writeFile</code> to defer to the mock, and providing the names of the functions as strings helps monad-mock to produce useful error messages upon failure. Internally, <code>MockT</code> is a <code>StateT</code> that keeps track of a list of <code>WithResult f</code> values as its state. Each call to the mock checks the action against the internal list of calls, and if they match, it returns the associated result. Otherwise, it throws an exception.</p><p>This scheme is simple, but it seems to work remarkably well. There are some obvious enhancements that will probably be eventually necessary, like allowing action results that run in the underlying monad <code>m</code> in order to support things like <code>throwError</code> from <code>MonadError</code>, but so far, it hasn’t been necessary for what we’ve been using it for. Certain tricky signatures defy this simple technique, such as signatures where a monadic action appears in a negative position (that is, the signatures you need things like <a href="https://hackage.haskell.org/package/monad-control">monad-control</a> or <a href="https://hackage.haskell.org/package/monad-unlift">monad-unlift</a> for), but we’ve found that most of our effects don’t have any reason to include such signatures.</p><h2><a name="a-brief-comparison-with-free-r-monads"></a>A brief comparison with free(r) monads</h2><p>At this point, astute readers will likely be thinking about free monads, which parts of this technique greatly resemble. The representation of actions as GADTs is especially similar to <a href="https://hackage.haskell.org/package/freer">freer</a>, which does something extremely similar. Indeed, you can think of this technique as something that combines a freer-style representation with mtl-style classes. Given that freer already does this, you might ask yourself what the point is.</p><p>If you are already sold on free monads, monad-mock may very well be uninteresting to you. From the perspective of theoretical novelty, monad-mock is not anything new or different. However, there are a variety of practical reasons to prefer mtl over free, and it’s nice to see how easy it is to enjoy the testing benefits of free without too much extra effort.</p><p>An in-depth comparison between mtl and free is well outside the scope of this blog post. However, the key point is that this technique <em>only</em> affects test code, so the real runtime implementation will not be affected in any way. This means you can take advantage of the performance benefits and ecosystem support of mtl without sacrificing simple, expressive testing.</p><h2><a name="conclusion"></a>Conclusion</h2><p>To cap things off, I want to emphasize monad-mock’s role as a single part of a larger initiative we’ve been making for the better part of the past eighteen months. Haskell is a language with ever-evolving techniques and style, and it’s sometimes dizzying to figure out how to use all the pieces together to develop robust, maintainable applications. While monad-mock might not be anything drastically different from existing testing techniques, my hope is that it can provide an opinionated mechanism to make testing easy and accessible, even for complex interactions with other services and systems.</p><p>I’ve made an effort to make it abundantly clear in this blog post that monad-mock is <em>not</em> a silver bullet to testing, and in fact, I would prefer other techniques for ensuring correctness whenever possible. Even so, mocking is a nice tool to have in your toolbox, and it’s a good fallback to get even the worst APIs under test coverage.</p><p>If you want to try out monad-mock for yourself, <a href="https://hackage.haskell.org/package/monad-mock">take a look at the documentation on Hackage</a> and start playing around! It’s still early software, so it’s not the most proven or featureful, but we’ve managed to get mileage out of it already, all the same. If you find any problems, have a use case it does not support, or just find something about it unclear, please do not hesitate to <a href="https://github.com/cjdev/monad-mock">open an issue on the GitHub repository</a>—we obviously can’t fix issues we don’t know about.</p><p>Thanks as always to the many people who have contributed ideas that have shaped my philosophy and approach to testing and have helped provide the tools that make this library work. Happy testing!</p><ol class="footnotes"></ol></article>Realizing Hackett, a metaprogrammable Haskellhttps://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/https://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/27 May 2017<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Lifts for free: making mtl typeclasses derivablehttps://lexi-lambda.github.io/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/https://lexi-lambda.github.io/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/28 Apr 2017<article><p>Perhaps the most important abstraction a Haskell programmer must understand to effectively write modern Haskell code, beyond the level of the monad, is the <em>monad transformer</em>, a way to compose monads together in a limited fashion. One frustrating downside to monad transformers is a proliferation of <code>lift</code>s, which explicitly indicate which monad in a transformer “stack” a particular computation should run in. Fortunately, the venerable <a href="https://hackage.haskell.org/package/mtl">mtl</a> provides typeclasses that make this lifting mostly automatic, using typeclass machinery to insert <code>lift</code> where appropriate.</p><p>Less fortunately, the mtl approach does not actually eliminate <code>lift</code> entirely, it simply moves it from use sites to instances. This requires a small zoo of extraordinarily boilerplate-y instances, most of which simply implement each typeclass method using <code>lift</code>. While we cannot eliminate the instances entirely without somewhat dangerous techniques like <a href="https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#overlapping-instances">overlapping instances</a>, we <em>can</em> automatically derive them using features of modern GHC, eliminating the truly unnecessary boilerplate.</p><h2><a name="the-problem-with-mtl-style-typeclasses"></a>The problem with mtl-style typeclasses</h2><p>To understand what problem it is exactly that we’re trying to solve, we first need to take a look at an actual mtl-style typeclass. I am going to start with an mtl-<em>style</em> typeclass, rather than an actual typeclass in the mtl, due to slight complications with mtl’s actual typeclasses that we’ll get into later. Instead, let’s start with a somewhat boring typeclass, which we’ll call <code>MonadExit</code>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">System.Exit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitCode</span><span class="p">)</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is a simple typeclass that abstracts over the concept of early exit, given an exit code. The most obvious implementation of this typeclass is over <code>IO</code>, which will actually exit the program:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="k">qualified</span><span class="w"> </span><span class="nn">System.Exit</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">IO</span><span class="w"> </span><span class="p">(</span><span class="n">exitWith</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IO</span><span class="o">.</span><span class="n">exitWith</span></code></pre><p>One of the cool things about these typeclasses, though, is that we don’t have to have just one implementation. We could also write a pure implementation of <code>MonadExit</code>, which would simply short-circuit the current computation and return the <code>ExitCode</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">ExitCode</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span></code></pre><p>Instead of simply having an instance on a concrete monad, though, we probably want to be able to use this in a larger monad stack, so we can define an <code>ExitT</code> monad transformer that can be inserted into any monad transformer stack:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Except</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="p">,</span><span class="w"> </span><span class="nf">runExceptT</span><span class="p">,</span><span class="w"> </span><span class="nf">throwError</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Trans</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTrans</span><span class="p">)</span> + +<span class="nf">runExitT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">runExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span></code></pre><p>With this in place, we can write actual programs using our <code>ExitT</code> monad transformer:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">runExitT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">putStrLn</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">putStrLn</span><span class="w"> </span><span class="s">"world"</span> +<span class="nf">hello</span> +<span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>This is pretty cool! Unfortunately, experienced readers will see the rather large problem with what we have so far. Specifically, it won’t actually work if we try and wrap <code>ExitT</code> in another monad transformer:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runExitT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="n">runReaderT</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">password</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"password1234"</span><span class="p">)</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="c1">-- super secure password</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"access granted"</span> + +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"not the right password"</span> +<span class="o">&lt;</span><span class="n">interactive</span><span class="o">&gt;:</span><span class="w"> </span><span class="ne">error</span><span class="kt">:</span> +<span class="w"> </span><span class="err">•</span><span class="w"> </span><span class="kt">No</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="n">for</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">[</span><span class="kt">Char</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m0</span><span class="p">)))</span> +<span class="w"> </span><span class="n">arising</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">use</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="err">‘</span><span class="n">it</span><span class="err">’</span> +<span class="w"> </span><span class="err">•</span><span class="w"> </span><span class="kt">In</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">stmt</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">interactive</span><span class="w"> </span><span class="kt">GHCi</span><span class="w"> </span><span class="n">command</span><span class="kt">:</span><span class="w"> </span><span class="n">print</span><span class="w"> </span><span class="n">it</span></code></pre><p>The error message is relatively self-explanatory if you are familiar with mtl error messages: there is no <code>MonadExit</code> instance for <code>ReaderT</code>. This makes sense, since we only defined a <code>MonadExit</code> instance for <em><code>ExitT</code></em>, nothing else. Fortunately, the instance for <code>ReaderT</code> is completely trivial, since we just need to use <code>lift</code> to delegate to the next monad in the stack:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>Now that the delegating instance is set up, we can actually use our <code>logIn</code> function:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"not the right password"</span> +<span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"password1234"</span> +<span class="kt">Right</span><span class="w"> </span><span class="s">"access granted"</span></code></pre><h3><a name="an-embarrassment-of-instances"></a>An embarrassment of instances</h3><p>We’ve managed to make our program work properly now, but we’ve still only defined the delegating instance for <code>ReaderT</code>. What if someone wants to use <code>ExitT</code> with <code>WriterT</code>? Or <code>StateT</code>? Or any of <code>ExceptT</code>, <code>RWST</code>, or <code>ContT</code>? Well, we have to define instances for each and every one of them, and as it turns out, the instances are all identical!</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ContT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>This is bad enough on its own, but this is actually the <em>simplest</em> case: a typeclass with a single method which is trivially lifted through any other monad transformer. Another thing we’ve glossed over is actually defining all the delegating instances for the <em>other</em> mtl typeclasses on <code>ExitT</code> itself. Fortunately, we can derive these ones with <code>GeneralizedNewtypeDeriving</code>, since <code>ExceptT</code> has already done most of the work for us:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="w"> </span><span class="c1">-- base</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="c1">-- transformers-base</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTrans</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="c1">-- mtl</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadThrow</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadCatch</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadMask</span><span class="w"> </span><span class="c1">-- exceptions</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="c1">-- monad-control</span> +<span class="w"> </span><span class="p">)</span></code></pre><p>Unfortunately, we have to write the <code>MonadError</code> instance manually if we want it, since we don’t want to pick up the instance from <code>ExceptT</code>, but rather wish to defer to the underlying monad. This means writing some truly horrid delegation code:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span> + +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x&#39;</span></code></pre><p>(Notably, this is so awful because <code>catchError</code> is more complex than the simple <code>exitWith</code> method we’ve studied so far, which is why we’re starting with a simpler typeclass. We’ll get more into this later, as promised.)</p><p>This huge number of instances is sometimes referred to as the “n<sup>2</sup> instances” problem, since it requires every monad transformer have an instance of every single mtl-style typeclass. Fortunately, in practice, this proliferation is often less horrible than it might seem, mostly because deriving helps a lot. However, remember that if <code>ExitT</code> <em>weren’t</em> a simple wrapper around an existing monad transformer, we wouldn’t be able to derive the instances at all! Instead, we’d have to write them all out by hand, just like we did with all the <code>MonadExit</code> instances.</p><p>It’s a shame that these typeclass instances can’t be derived in a more general way, allowing derivation for arbitrary monad transformers instead of simply requiring the newtype deriving machinery. As it turns out, with clever use of modern GHC features, we actually <strong>can</strong>. It’s not even all that hard.</p><h2><a name="default-instances-with-default-signatures"></a>Default instances with default signatures</h2><p>It’s not hard to see that our <code>MonadExit</code> instances are all exactly the same: just <code>lift . exitWith</code>. Why is that, though? Well, every instance is an instance on a monad transformer over a monad that is already an instance of <code>MonadExit</code>. In fact, we can express this in a type signature, and we can extract <code>lift . exitWith</code> into a separate function:</p><pre><code class="pygments"><span class="nf">defaultExitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">defaultExitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>However, writing <code>defaultExitWith</code> really isn’t any easier than writing <code>lift . exitWith</code>, so this deduplication doesn’t really buy us anything. However, it <em>does</em> indicate that we could write a default implementation of <code>exitWith</code> if we could require just a little bit more from the implementing type. With <a href="https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#default-method-signatures">GHC’s <code>DefaultSignatures</code> extension</a>, we can do precisely that.</p><p>The idea is that we can write a separate type signature for a default implementation of <code>exitWith</code>, which can be more specific than the type signature for <code>exitWith</code> in general. This allows us to use our <code>defaultExitWith</code> implementation more or less directly:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DefaultSignatures #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>We have to use <code>m1</code> instead of <code>m</code>, since type variables in the instance head are always scoped, and the names would conflict. However, this creates another problem, since our specialized type signature replaces <code>m</code> with <code>t m1</code>, which won’t quite work (as GHC can’t automatically figure out they should be the same). Instead, we can use <code>m</code> in the type signature, then just add a type equality constraint ensuring that <code>m</code> and <code>t m1</code> must be the same type:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>Now we can write all of our simple instances without even needing to write a real implementation! All of the instance bodies can be empty:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ContT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>While this doesn’t completely alleviate the pain of writing instances, it’s definitely an improvement over what we had before. With <a href="https://downloads.haskell.org/~ghc/8.2.1-rc1/docs/html/users_guide/glasgow_exts.html#deriving-strategies">GHC 8.2’s new <code>DerivingStrategies</code> extension</a>, it becomes especially beneficial when defining entirely new transformers that should also have <code>ExitT</code> instances, since they can be derived with <code>DeriveAnyClass</code>:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="n">anyclass</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="p">)</span></code></pre><p>This is pretty wonderful.</p><p>Given that only <code>MonadExit</code> supports being derived in this way, we sadly still need to implement the other, more standard mtl-style typeclasses ourselves, like <code>MonadIO</code>, <code>MonadBase</code>, <code>MonadReader</code>, <code>MonadWriter</code>, etc. However, what if all of those classes provided the same convenient default signatures that our <code>MonadExit</code> does? If that were the case, then we could write something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="n">anyclass</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadThrow</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadCatch</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadMask</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span> +<span class="w"> </span><span class="p">)</span></code></pre><p>Compared to having to write all those instances by hand, this would be a pretty enormous difference. Unfortunately, many of these typeclasses are not quite as simple as our <code>MonadExit</code>, and we’d have to be a bit more clever to make them derivable.</p><h2><a name="making-mtl-s-classes-derivable"></a>Making mtl’s classes derivable</h2><p>Our <code>MonadExit</code> class was extremely simple, since it only had a single method with a particularly simple type signature. For reference, this was the type of our generic <code>exitWith</code>:</p><pre><code class="pygments"><span class="nf">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Let’s now turn our attention to <code>MonadReader</code>. At first blush, this typeclass should not be any trickier to implement than <code>MonadExit</code>, since the types of <code>ask</code> and <code>reader</code> are both quite simple:</p><pre><code class="pygments"><span class="nf">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="nf">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>However, the type of the other method, <code>local</code>, throws a bit of a wrench in our plans. It has the following type signature:</p><pre><code class="pygments"><span class="nf">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Why is this so much more complicated? Well, the key is in the second argument, which has the type <code>m a</code>. That’s not something that can be simply <code>lift</code>ed away! Try it yourself: try to write a <code>MonadReader</code> instance for some monad transformer. It’s not as easy as it looks!</p><p>We can illustrate the problem by creating our own version of <code>MonadReader</code> and implementing it for something like <code>ExceptT</code> ourselves. We can start with the trivial methods first:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span></code></pre><p>However, implementing <code>local</code> is harder. Let’s specialize the type signature to <code>ExceptT</code> to make it more clear why:</p><pre><code class="pygments"><span class="nf">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Our base monad, <code>m</code>, implements <code>local</code>, but we have to convert the first argument from <code>ExceptT e m a</code> into <code>m (Either e a)</code> first, run it through <code>local</code> in <code>m</code>, then wrap it back up in <code>ExceptT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>This operation is actually a mapping operation of sorts, since we’re mapping <code>local f</code> over <code>x</code>. For that reason, this can be rewritten using the <code>mapExceptT</code> function provided from <code>Control.Monad.Except</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapExceptT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">local</span></code></pre><p>If you implement <code>MonadReader</code> instances for other transformers, like <code>StateT</code> and <code>WriterT</code>, you’ll find that the instances are exactly the same <em>except</em> for <code>mapExceptT</code>, which is replaced with <code>mapStateT</code> and <code>mapWriterT</code>, respectively. This is sort of obnoxious, given that we want to figure out how to create a generic version of <code>local</code> that works with any monad transformer, but this requires concrete information about which monad we’re in. Obviously, the power <code>MonadTrans</code> gives us is not enough to make this generic. Fortunately, there is a typeclass which does: <a href="http://hackage.haskell.org/package/monad-control-1.0.1.0/docs/Control-Monad-Trans-Control.html#t:MonadTransControl"><code>MonadTransControl</code></a> from the <code>monad-control</code> package.</p><p>Using <code>MonadTransControl</code>, we can write a generic <code>mapT</code> function that maps over an arbitrary monad transformer with a <code>MonadTransControl</code> instance:</p><pre><code class="pygments"><span class="nf">mapT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="p">),</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span> +<span class="nf">mapT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">return</span></code></pre><p>This type signature may look complicated (and, well, it is), but the idea is that the <code>StT</code> associated type family encapsulates the monadic state that <code>t</code> introduces. For example, for <code>ExceptT</code>, <code>StT (ExceptT e) a</code> is <code>Either e a</code>. For <code>StateT</code>, <code>StT (StateT s) a</code> is <code>(a, s)</code>. Some transformers, like <code>ReaderT</code>, have no state, so <code>StT (ReaderT r) a</code> is just <code>a</code>.</p><p>I will not go into the precise mechanics of how <code>MonadTransControl</code> works in this blog post, but it doesn’t matter significantly; the point is that we can now use <code>mapT</code> to create a generic implementation of <code>local</code> for use with <code>DefaultSignatures</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> + +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">local</span> + +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">ask</span></code></pre><p>Once more, we now get instances of our typeclass, in this case <code>MonadReader</code>, <strong>for free</strong>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>It’s also worth noting that we <em>don’t</em> get a <code>ContT</code> instance for free, even though <code>ContT</code> has a <code>MonadReader</code> instance in mtl. Unlike the other monad transformers mtl provides, <code>ContT</code> does not have a <code>MonadTransControl</code> instance because it cannot be generally mapped over. While a <code>mapContT</code> function does exist, its signature is more restricted:</p><pre><code class="pygments"><span class="nf">mapContT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ContT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ContT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>It happens that <code>local</code> can still be implemented for <code>ContT</code>, so it can still have a <code>MonadReader</code> instance, but it cannot be derived in the same way as it can for the other transformers. Still, in practice, I’ve found that most user-defined transformers do not have such complex control flow, so they can safely be instances of <code>MonadTransControl</code>, and they get this deriving for free.</p><h3><a name="extending-this-technique-to-other-mtl-typeclasses"></a>Extending this technique to other mtl typeclasses</h3><p>The default instances for the other mtl typeclasses are slightly different from the one for <code>MonadReader</code>, but for the most part, the same general technique applies. Here’s a derivable <code>MonadError</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span> + +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">f</span><span class="p">))</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">return</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>The <code>MonadState</code> interface turns out to be extremely simple, so it doesn’t even need <code>MonadTransControl</code> at all:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">get</span> + +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">put</span> + +<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>Everything seems to be going well! However, not everything is quite so simple.</p><h3><a name="a-monadwriter-diversion"></a>A <code>MonadWriter</code> diversion</h3><p>Unexpectedly, <code>MonadWriter</code> turns out to be by far the trickiest of the bunch. It’s not too hard to create default implementations for most of the methods of the typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="p">(</span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">writer</span> + +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tell</span> + +<span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="n">y&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="p">(</span><span class="n">return</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">y&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span></code></pre><p>However, <code>MonadWriter</code> has a fourth method, <code>pass</code>, which has a particularly tricky type signature:</p><pre><code class="pygments"><span class="nf">pass</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>As far as I can tell, this is not possible to generalize using <code>MonadTransControl</code> alone, since it would require inspection of the result of the monadic argument (that is, it would require a function from <code>StT t (a, b) -&gt; (StT t a, b)</code>), which is not possible in general. My gut is that this could likely also be generalized with a slightly more powerful abstraction than <code>MonadTransControl</code>, but it is not immediately obvious to me what that abstraction should be.</p><p>One extremely simple way to make this possible would be to design something to serve this specific use case:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">RunSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="o">.</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="kt">RunSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Instances of <code>MonadTransSplit</code> would basically just provide a way to pull out bits of the result, if possible:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">Nothing</span><span class="p">)</span> +<span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">),</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">),</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span></code></pre><p>Then, using this, it would be possible to write a generic version of <code>pass</code>:</p><pre><code class="pygments"><span class="kr">default</span><span class="w"> </span><span class="n">pass</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">pass</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pass</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="kr">case</span> +<span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">f</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Nothing</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="p">(</span><span class="n">return</span><span class="w"> </span><span class="n">r</span><span class="p">)</span></code></pre><p>However, this seems pretty overkill for just one particular method, given that I have no idea if <code>MonadTransSplit</code> would be useful <em>anywhere</em> else. One interesting thing about going down this rabbit hole, though, is that I learned that <code>pass</code> has some somewhat surprising behavior when mixed with transformers like <code>ExceptT</code> or <code>MaybeT</code>, if you don’t carefully consider how it works. It’s a strange method with a somewhat strange interface, so I don’t think I have a satisfactory conclusion about <code>MonadWriter</code> yet.</p><h2><a name="regrouping-and-stepping-back"></a>Regrouping and stepping back</h2><p>Alright, that was a lot of fairly intense, potentially confusing code. What the heck did we actually accomplish? Well, we got a couple of things:</p><ol><li><p>First, we developed a technique for writing simple mtl-style typeclasses that are derivable using <code>DeriveAnyClass</code> (or simply writing an empty instance declaration). We used a <code>MonadExit</code> class as a proof of concept, but really, the technique is applicable to most mtl-style typeclasses that represent simple effects (including, for example, <code>MonadIO</code>).</p><p>This technique is useful in isolation, even if you completely disregard the rest of the blog post. For an example where I recently applied it in real code, see <a href="https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L226-L271">the default signatures provided with <code>MonadPersist</code> from the <code>monad-persist</code> library</a>, which make <a href="https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L506-L513">defining instances completely trivial</a>. If you use mtl-style typeclasses in your own application to model effects, I don’t see much of a reason <em>not</em> to use this technique.</p></li><li><p>After <code>MonadExit</code>, we applied the same technique to the mtl-provided typeclasses <code>MonadReader</code>, <code>MonadError</code>, and <code>MonadState</code>. These are a bit trickier, since the first two need <code>MonadTransControl</code> in addition to the usual <code>MonadTrans</code>.</p><p>Whether or not this sort of thing should actually be added to mtl itself probably remains to be seen. For the simplest typeclass, <code>MonadState</code>, it seems like there probably aren’t many downsides, but given the difficulty implementing it for <code>MonadWriter</code> (or, heaven forbid, <code>MonadCont</code>, which I didn’t even seriously take a look at for this blog post), it doesn’t seem like an obvious win. Consistency is important.</p><p>Another downside that I sort of glossed over is possibly even more significant from a practical point of view: adding default signatures to <code>MonadReader</code> would require the removal of the default implementation of <code>ask</code> that is provided by the existing library (which implements <code>ask</code> in terms of <code>reader</code>). This would be backwards-incompatible, so it’d be difficult to change, even if people wanted to do it. Still, it’s interesting to consider what these typeclasses might look like if they were designed today.</p></li></ol><p>Overall, these techniques are not a silver bullet for deriving mtl-style typeclasses, nor do they eliminate the n<sup>2</sup> instances problem that mtl style suffers from. That said, they <em>do</em> significantly reduce boilerplate and clutter in the simplest cases, and they demonstrate how modern Haskell’s hierarchy of typeclasses provides a lot of power, both to describe quite abstract concepts and to alleviate the need to write code by hand.</p><p>I will continue to experiment with the ideas described in this blog post, and I’m sure some more pros and cons will surface as I explore the design space. If you have any suggestions for how to deal with “the <code>MonadWriter</code> problem”, I’d be very interested to hear them! In the meantime, consider using the technique in your application code when writing effectful, monadic typeclasses.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questionshttps://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/https://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/05 Jan 2017<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheseshttps://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/https://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/02 Jan 2017<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Using types to unit-test in Haskellhttps://lexi-lambda.github.io/blog/2016/10/03/using-types-to-unit-test-in-haskell/https://lexi-lambda.github.io/blog/2016/10/03/using-types-to-unit-test-in-haskell/03 Oct 2016<article><p>Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.</p><p>Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.</p><h2><a name="first-an-aside-on-testing-philosophy"></a>First, an aside on testing philosophy</h2><p>Testing methodology is a controversial topic within the larger programming community, and there are a multitude of different approaches. This blog post is about <em>unit testing</em>, an already nebulous term with a number of different definitions. For the purposes of this post, I will define a unit test as a test that stubs out collaborators of the code under test in some way. Accomplishing that in Haskell is what this is primarily about.</p><p>I want to be clear that I do not think that unit tests are the only way to write tests, nor the best way, nor even always an applicable way. Depending on your domain, rigorous unit testing might not even make sense, and other forms of tests (end-to-end, integration, benchmarks, etc.) might fulfill your needs.</p><p>In practice, though, implementing those other kinds of tests seems to be well-documented in Haskell compared to pure, object-oriented style unit testing. As my Haskell applications have grown, I have found myself wanting a more fine-grained testing tool that allows me to both test a piece of my codebase in isolation and also use my domain-specific types. This blog post is about that.</p><p>With that disclaimer out of the way, let’s talk about testing in Haskell.</p><h2><a name="drawing-seams-using-types"></a>Drawing seams using types</h2><p>One of the primary attributes of unit tests in object-oriented languages, especially statically-typed ones, is the concept of “seams” within a codebase. These are internal boundaries between components of a system. Some boundaries are obvious—interactions with a database, manipulation of the file system, and performing I/O over the network, to name a few examples—but others are more subtle. Especially in larger codebases, it can be helpful to isolate two related but distinct pieces of functionality as much as possible, which makes them easier to reason about, even if they’re actually part of the same codebase.</p><p>In OO languages, these seams are often marked using interfaces, whether explicitly (in the case of static languages) or implicitly (in the case of dynamic ones). By programming to an interface, it’s possible to create “fake” implementations of that interface for use in unit tests, effectively making it possible to stub out code that isn’t directly relevant to the code being tested.</p><p>In Haskell, representing these seams is a lot less obvious. Consider a fairly trivial function that reverses a file’s contents on the file system:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span></code></pre><p>This function is impossible to test without testing against a real file system. It simply performs I/O directly, and there’s no way to “mock out” the file system for testing purposes. Now, admittedly, this function is so trivial that a unit test might not seem worth the cost, but consider a slightly more complicated function that interacts with a database:</p><pre><code class="pygments"><span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>It might now be a bit more clear that it could be useful to test the above function without running a real database and doing all the necessary context setup before each test case. Indeed, it would be nice if a test could just provide stubbed implementations for <code>fetchUser</code> and <code>fetchRecentPosts</code>, then make assertions about the output.</p><p>One way to solve this problem is to pass the results of those two functions to <code>renderUserProfile</code> as arguments, turning it into a pure function that could be easily tested. This becomes obnoxious for functions of even just slightly more complexity, though (it is not unreasonable to imagine needing a handful of different queries to render a user’s profile page), and it requires significantly restructuring code simply because the tests need it.</p><p>The above code is not only difficult to test, however—it has another problem, too. Specifically, both functions return <code>IO</code> values, which means they can effectively do <em>anything</em>. Haskell has a very strong type system for typing terms, but it doesn’t provide any guarantees about effects beyond a simple yes/no answer about function purity. Even though the <code>renderUserProfile</code> function should really only need to interact with the database, it could theoretically delete files, send emails, make HTTP requests, or do any number of other things.</p><p>Fortunately, it’s possible to solve <em>both</em> problems—a lack of testability and a lack of type safety—using the same general technique. This approach is reminiscent of the interface-based seams of object-oriented languages, but unlike most object-oriented approaches, it provides additional type safety guarantees without the need to explicitly modify the code to support some kind of dependency injection.</p><h3><a name="making-implicit-interfaces-explicit"></a>Making implicit interfaces explicit</h3><p>Statically typed, object-oriented languages provide interfaces as a language construct to encode certain kinds of contracts into the type system, and Haskell has something similar. Typeclasses are, in many ways, an analog to OO interfaces, and they can be used in a similar way. In the above case, let’s write down interfaces that the <code>reverseFile</code> and <code>renderUserProfile</code> functions can use:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span></code></pre><p>The really nice thing about these interfaces is that our function implementations don’t have to change <em>at all</em> to take advantage of them. In fact, all we have to change is their types:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span> + +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty neat, since we haven’t had to alter our code at all, but we’ve managed to completely decouple ourselves from <code>IO</code>. This has the direct effect of both making our code more abstract (we no longer rely on the “real” file system or a “real” database, which makes our code easier to test) and restricting what our functions can do (just from looking at the type signatures, we know what side-effects they can perform).</p><p>Of course, since we’re now coding against an interface, our code doesn’t actually do much of anything. If we want to actually use the functions we’ve written, we’ll have to define instances of <code>MonadFS</code> and <code>MonadDB</code>. When actually running our code, we’ll probably still use <code>IO</code> (or some monad transformer stack with <code>IO</code> at the bottom), so we can define trivial instances for that existing use case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchUser</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchRecentPosts</span></code></pre><p>Even if we go no further, <strong>this is already incredibly useful</strong>. By restricting the sorts of effects our functions can perform at the type level, it becomes a lot easier to see which code is interacting with what. This can be invaluable when working in a part of a moderately large codebase that you are unfamiliar with. Even if the only instance of these typeclasses is <code>IO</code>, the benefits are immediately apparent.</p><p>Of course, this blog post is about testing, so we’re going to go further and take advantage of these seams we’ve now drawn. The question is: how?</p><h2><a name="testing-with-typeclasses-an-initial-attempt"></a>Testing with typeclasses: an initial attempt</h2><p>Given that we now have functions depending on an interface instead of <code>IO</code>, we can create separate instances of our typeclasses for use in tests. Let’s start with the <code>renderUserProfile</code> function. We’ll create a simple wrapper around the <code>Identity</code> type, since we don’t actually care much about the “effects” of our <code>MonadDB</code> methods:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Functor.Identity</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">unTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now, we’ll create a trivial instance of <code>MonadDB</code> for <code>TestM</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa"</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">Post</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">postTitle</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span></code></pre><p>With this instance, it’s now possible to write a simple unit test of the <code>renderUserProfile</code> function that doesn’t need a real database running at all:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"renderUserProfile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows the user’s name"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="s">"Alyssa’s Profile"</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows a list of the user’s posts"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">li</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty nice, and running the above tests reveals a nice property of these kinds of isolated test cases: the test suite runs <em>really, really fast</em>. Communicating with a database, even in extremely simple ways, takes a measurable amount of time, especially with dozens of tests. In contrast, even with hundreds of tests, our unit test suite runs in less than a tenth of a second.</p><p>This all seems to be successful, so let’s try and apply the same testing technique to <code>reverseFile</code>.</p><h3><a name="testing-side-effectful-code"></a>Testing side-effectful code</h3><p>Looking at the type signature for <code>reverseFile</code>, we have a small problem:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Specifically, the return type is <code>()</code>. Making any assertions against the result of this function would be completely worthless, given that it’s guaranteed to be the same exact thing each time. Instead, <code>reverseFile</code> is inherently side-effectful, so we want to be able to test that it properly interacts with the file system in the correct way.</p><p>In order to do this, a simple wrapper around <code>Identity</code> won’t be enough, but we can replace it with something more powerful: <code>Writer</code>. Specifically, we can use a writer monad to “log” what gets called in order to test side-effects. We’ll start by creating a new <code>TestM</code> type, just like last time:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">])</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span></code></pre><p>Using this slightly more powerful type, we can write a useful instance of <code>MonadFS</code> that will track the argument given to <code>writeFile</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span></code></pre><p>Again, the instance is quite simple, but it now enables us to write a straightforward unit test for <code>reverseFile</code>:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span></code></pre><p>Again, quite simple to both implement and use, and the test itself is blindingly fast. There’s another problem, though, which is that we have technically left part of <code>reverseFile</code> untested: we’ve completely ignored the <code>path</code> argument.</p><p>In this contrived example, it may seem silly to test something so trivial, but in real code, it’s quite possible that one would care very much about testing multiple different aspects about a single function. When testing <code>renderUserProfile</code>, this was not hard, since we could reuse the same <code>TestM</code> type and <code>MonadDB</code> instance for both test cases, but in the <code>reverseFile</code> example, we’ve ignored the path entirely.</p><p>We <em>could</em> adjust our <code>MonadFS</code> instance to also track the path provided to each method, but this has a few problems. First, it means every test case would depend on all the various properties we are testing, which would mean updating every test case when we add a new one. It would also be simply impossible if we needed to track multiple types—in this particular case, it turns out that <code>String</code> and <code>FilePath</code> are actually the same type, but in practice, there may be a handful of disparate, incompatible types.</p><p>Both of the above issues could be fixed by creating a sum type and manually filtering out the relevant elements in each test case, but a much more intuitive approach would be to simply have a separate instance for each case. Unfortunately, in Haskell, creating a new instance means creating an entirely new type. To illustrate how much duplication that would entail, we could create the following type and instance for testing proper propagation of the <code>path</code> argument:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">])</span> + +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span></code></pre><p>Now it’s possible to add an extra test case that asserts that the proper path is provided to the two filesystem functions:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This works, but it’s ultimately unacceptably complicated. Our test harness code is now significantly larger than the actual tests themselves, and the amount of boilerplate is frustrating. Verbose test suites are especially bad, since forcing programmers to jump through hoops just to implement a single test reduces the likelihood that people will actually write good tests, if they write tests at all. In contrast, if writing tests is easy, then people will naturally write more of them.</p><p>The above strategy to writing tests is not good enough, but it does reveal a particular problem: in Haskell, typeclass instances are not first-class values that can be manipulated and abstracted over, they are static constructs that can only be managed by the compiler, and users do not have a direct way to modify them. With some cleverness, however, we can actually create an approximation of first-class typeclass dictionaries, which will allow us to dramatically simplify the above testing mechanism.</p><h2><a name="creating-first-class-typeclass-instances"></a>Creating first-class typeclass instances</h2><p>In order to provide an easy way to construct instances, we need a way to represent instances as ordinary Haskell values. This is not terribly difficult, given that instances are conceptually just records containing a collection of functions. For example, we could create a datatype that represents an instance of the <code>MonadFS</code> typeclass:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>To avoid namespace clashes with the actual method identifiers, the record fields are prefixed with an underscore, but otherwise, the translation is remarkably straightforward. Using this record type, we can easily create values that represent the two instances we defined above:</p><pre><code class="pygments"><span class="nf">contentInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> + +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>These two values represent two different implementations of <code>MonadFS</code>, but since they’re ordinary Haskell values, they can be manipulated and even <em>extended</em> like any other records. This can be extremely useful, since it makes it possible to create a sort of “base” instance, then have individual test cases override individual pieces of functionality piecemeal.</p><p>Of course, although we’ve written these two instances, we have no way to actually use them. After all, Haskell does not provide a way to explicitly provide typeclass dictionaries. Fortunately, we can create a sort of “proxy” type that will use a reader to thread the dictionary around explicitly, and the instance can defer to the dictionary’s implementation.</p><h3><a name="creating-an-instance-proxy"></a>Creating an instance proxy</h3><p>To represent our proxy type, we’ll use a combination of a <code>Writer</code> and a <code>ReaderT</code>; the former to implement the logging used by instances, and the latter to actually thread around the dictionary. Our type will look like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">log</span> +<span class="w"> </span><span class="p">)</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="n">inst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">inst</span><span class="p">)</span></code></pre><p>This might look rather complicated, and it is, but let’s break down exactly what it’s doing.</p><ol><li><p>The <code>TestM</code> type includes two type parameters. The first is the type of value that will be logged (hence the name <code>log</code>), which corresponds to the argument to <code>Writer</code> from previous incarnations of <code>TestM</code>. Unlike those types, though, we want this version to work with any <code>Monoid</code>, so we’ll make it a type parameter. The second parameter is simply the type of the current monadic value, as before.</p></li><li><p>The type itself is defined as a wrapper around a small monad transformer stack, the first of which is <code>ReaderT</code>. The state threaded around by the reader is, in this case, the instance dictionary, which is <code>MonadFSInst</code>.</p><p>However, recall that <code>MonadFSInst</code> accepts a type variable—the type of a monad itself—so we must provide <code>TestM log</code> as an argument to <code>MonadFSInst</code>. This slight bit of indirection allows us to tie the knot between the mutually dependent instances and proxy type.</p></li><li><p>The base monad in the transformer stack is <code>Writer</code>, which is used to actually implement the logging functionality, just like in prior cases. The only difference now is that the <code>log</code> type parameter now determines what the writer actually produces.</p></li><li><p>Finally, as before, we use <code>GeneralizedNewtypeDeriving</code> to derive all the relevant <code>mtl</code> classes, adding the somewhat wordy <code>MonadReader</code> constraint to the list.</p></li></ol><p>Using this single type, we can now implement a <code>MonadFS</code> instance that defers to the dictionary carried around within <code>TestM</code>’s reader state:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_readFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_writeFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This may seem somewhat boilerplate-y, and it is to some extent, but the important consideration is that this boilerplate only needs to be written <em>once</em>. With this in place, it’s now possible to write an arbitrary number of first-class instances that use the above mechanism without extending the mechanism at all.</p><p>To see what actually using this code would look like, let’s update the <code>reverseFile</code> tests to use the new <code>TestM</code> implementation, as well as the <code>contentInst</code> and <code>pathInst</code> dictionaries from earlier:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>We can do a little bit better, though. Really, the definitions of <code>contentInst</code> and <code>pathInst</code> are specific to each test case. With ordinary typeclass instances, we cannot scope them to any particular block, but since <code>MonadFSInst</code> is just an ordinary Haskell datatype, we can manipulate them just like any other Haskell values. Therefore, we can just inline those instances’ definitions into the test cases themselves to keep them closer to the actual tests.</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This is pretty good. We’re now able to create inline instances of our <code>MonadFS</code> typeclass, which allows us to write extremely concise unit tests using ordinary Haskell typeclasses as system seams. We’ve managed to cut down on the boilerplate considerably, though we still have a couple problems. For one, this example only uses a single typeclass containing only two methods. A real <code>MonadFS</code> typeclass would likely have at least a dozen methods for performing various filesystem operations, and writing out the instance dictionaries for every single method, even the ones that aren’t used within the code under test, would be pretty frustratingly verbose.</p><p>This problem is solvable, though. Since instances are just ordinary Haskell records, we can create a “base” instance that just throws an exception whenever the method is called:</p><pre><code class="pygments"><span class="nf">baseInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">baseInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_readFile’"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_writeFile’"</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Then code that only uses <code>readFile</code> could only override that particular method, for example:</p><pre><code class="pygments"><span class="kr">let</span><span class="w"> </span><span class="n">myInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">baseInst</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span></code></pre><p>Normally, of course, this would be a terrible idea. However, since this is all just test code, it can be extremely useful in quickly figuring out what methods need to be stubbed out for a particular test case. Since all the code actually gets run at test time, attempts to use unimplemented instance methods will immediately raise an error, informing the programmer which methods need to be implemented to make the test pass. This can also help to significantly cut down on the amount of effort it takes to implement each test.</p><p>Another problem is that our approach is specialized exclusively to <code>MonadFS</code>. What about functions that use both <code>MonadFS</code> <em>and</em> <code>MonadDB</code>, for example? Fortunately, that is not hard to solve, either. We can adapt the <code>MonadFSInst</code> type to include fields for all of the typeclasses relevant to our system, turning it into a generic test fixture of sorts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FixtureInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FixtureInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">-- MonadFS</span> +<span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="c1">-- MonadDB</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Updating <code>TestM</code> to use <code>FixtureInst</code> instead of <code>MonadFSInst</code> is trivial, and all the rest of the infrastructure still works. However, this means that every time a new typeclass is added, three things need to be updated:</p><ol><li><p>Its methods need to be added to the <code>FixtureInst</code> record.</p></li><li><p>Those methods need to be given error-raising defaults in the <code>baseInst</code> value.</p></li><li><p>An actual instance of the typeclass needs to be written for <code>TestM</code> that defers to the <code>FixtureInst</code> value.</p></li></ol><p>Furthermore, most of this manual manipulation of methods is required every time a particular typeclass changes, whether that means adding a method, removing a method, renaming a method, or changing a method’s type. This is especially frustrating given that all this code is really just mechanical boilerplate that could all be derived by the set of typeclasses being tested.</p><p>That last point is especially important: aside from the instances themselves, every piece of boilerplate above is obviously possible to generate from existing types alone. With that piece of information in mind, we can do even better: we can use Template Haskell.</p><h2><a name="removing-the-boilerplate-using-test-fixture"></a>Removing the boilerplate using <code>test-fixture</code></h2><p>The above code was not only rather boilerplate-heavy, it was pretty complicated. Fortunately, you don’t actually have to write it. Enter the library <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code></a>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture.TH</span> + +<span class="nf">mkFixture</span><span class="w"> </span><span class="s">"FixtureInst"</span><span class="w"> </span><span class="p">[</span><span class="kt">&#39;&#39;MonadFS</span><span class="p">,</span><span class="w"> </span><span class="kt">&#39;&#39;MonadDB</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">contentInst</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">pathInst</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p><strong>That’s it.</strong> The above code automatically generates everything you need to write fast, simple, deterministic unit tests in Haskell. The <code>mkFixture</code> function is a Template Haskell macro that expands into a definition quite similar to the <code>FixtureInst</code> type we wrote by hand, but since it’s automatically generated from the typeclass definitions, it never needs to be updated.</p><p>The <code>logTestFixture</code> function replaces the <code>logTestM</code> function we wrote by hand, but it works exactly the same. The <code>Control.Monad.TestFixture</code> library also exports a <code>log</code> function that is a synonym for <code>tell . singleton</code>, but using <code>tell</code> directly still works if you prefer.</p><p>The <code>mkFixture</code> function also generates a <code>Default</code> instance, which replaces the <code>baseInst</code> value defined earlier. It functions the same way, though, producing useful error messages that refer to the names of unimplemented typeclass methods that have not been stubbed out.</p><p>This blog post is not a <code>test-fixture</code> tutorial—indeed, it is much more complicated than a <code>test-fixture</code> tutorial would be, since it covers what the library is really doing under the hood—but if you’re interested, I would highly recommend you take a look at the <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code> documentation on Hackage</a>.</p><h2><a name="conclusion-credits-and-similar-techniques"></a>Conclusion, credits, and similar techniques</h2><p>This blog post came about as the result of a need my coworkers and I found when writing Haskell code; we wanted a way to write unit tests quickly and easily, but we didn’t find much advice from the rest of the Haskell ecosystem. The <code>test-fixture</code> library is the result of that exploratory work, and we currently use it to test a significant portion of our Haskell code.</p><p>It would be extremely unfair to suggest that I was the inventor of this technique or the inventor of the library. Two of my coworkers, <a href="https://github.com/jxv">Joe Vargas</a> and <a href="https://github.com/aztecrex">Greg Wiley</a>, came up with the general approach and wrote <code>Control.Monad.TestFixture</code>, and I simply wrote the Template Haskell macro to eliminate the boilerplate. With that in mind, I think I can say with some fairness that I think this technique is a joy to use when unit testing is a desirable goal, and I would definitely recommend it if you are interested in doing isolated testing in Haskell.</p><p>The general technique of using typeclasses to emulate effects was in part inspired by the well-known <code>mtl</code> library. An alternate approach to writing unit-testable Haskell code is using free monads, but overall, I prefer this approach over free monads because the typeclass constraints add type safety in ways that free monads do not (at least not without additional boilerplate), and this approach also lends itself well to static analysis-based boilerplate reduction techniques. It has its own tradeoffs, though, so if you’ve had success with free monads, then I certainly make no claim this is a superior approach, just one that I’ve personally found pleasant.</p><p>As a final note, if you <em>do</em> check out <code>test-fixture</code>, feel free to leave feedback by opening issues on <a href="https://github.com/cjdev/test-fixture/issues">the GitHub issue tracker</a>—even things like confusing documentation are worth a bug report.</p><ol class="footnotes"></ol></article>Understanding the npm dependency modelhttps://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/https://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/24 Aug 2016<article><p>Currently, <a href="https://www.npmjs.com">npm</a> is <em>the</em> package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like <a href="https://bower.io">Bower</a> are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects <strong>you</strong> as a user or package developer.</p><h2><a name="first-the-basics"></a>First, the basics</h2><p>At a high level, npm is not too dissimilar from other package managers for programming languages: packages depend on other packages, and they express those dependencies with <em>version ranges</em>. npm happens to use the <a href="http://semver.org">semver</a> versioning scheme to express those ranges, but the way it performs version resolution is mostly immaterial; what matters is that packages can depend on ranges rather than specific versions of packages.</p><p>This is rather important in any ecosystem, since locking a library to a specific set of dependencies could cause significant problems, but it’s actually much less of a problem in npm’s case compared to other, similar package systems. Indeed, it is often safe for a library author to pin a dependency to a specific version without affecting dependent packages or applications. The tricky bit is determining <em>when</em> this is safe and when it’s not, and this is what I so frequently find that people get wrong.</p><h2><a name="dependency-duplication-and-the-dependency-tree"></a>Dependency duplication and the dependency tree</h2><p>Most users of npm (or at least most package authors) eventually learn that, unlike other package managers, npm installs a <em>tree</em> of dependencies. That is, every package installed gets its own set of dependencies rather than forcing every package to share the same canonical set of packages. Obviously, virtually every single package manager in existence has to model a dependency tree at some point, since that’s how dependencies are expressed by programmers.</p><p>For example, consider two packages, <code>foo</code> and <code>bar</code>. Each of them have their own set of dependencies, which can be represented as a tree:</p><pre><code>foo +├── hello ^0.1.2 +└── world ^1.0.7 + +bar +├── hello ^0.2.8 +└── goodbye ^3.4.0 +</code></pre><p>Imagine an application that depends on <em>both</em> <code>foo</code> and <code>bar</code>. Obviously, the <code>world</code> and <code>goodbye</code> dependencies are totally unrelated, so how npm handles them is relatively uninteresting. However, consider the case of <code>hello</code>: both packages require conflicting versions.</p><p>Most package managers (including RubyGems/Bundler, pip, and Cabal) would simply barf here, reporting a version conflict. This is because, in most package management models, <strong>only one version of any particular package can be installed at a time</strong>. In that sense, one of the package manager’s primary responsibilities is to figure out a set of package versions that will satisfy every version constraint simultaneously.</p><p>In contrast, npm has a somewhat easier job: it’s totally okay with installing different versions of the same package because each package gets its own set of dependencies. In the aforementioned example, the resulting directory structure would look something like this:</p><pre><code>node_modules/ +├── foo/ +│ └── node_modules/ +│ ├── hello/ +│ └── world/ +└── bar/ + └── node_modules/ + ├── hello/ + └── goodbye/ +</code></pre><p>Notably, the directory structure very closely mirrors the actual dependency tree. The above diagram is something of a simplification: in practice, each transitive dependency would have its own <code>node_modules</code> directory and so on, but the directory structure can get pretty messy pretty quickly. (Furthermore, npm 3 performs some optimizations to attempt to share dependencies when it can, but those are ultimately unnecessary to actually understanding the model.)</p><p>This model is, of course, extremely simple. The obvious effect is that every package gets its own little sandbox, which works absolutely marvelously for utility libraries like <code>ramda</code>, <code>lodash</code>, or <code>underscore</code>. If <code>foo</code> depends on <code>ramda@^0.19.0</code> but <code>bar</code> depends on <code>ramda@^0.22.0</code>, they can both coexist completely peacefully without any problems.</p><p>At first blush, this system is <em>obviously</em> better than the alternative, flat model, so long as the underlying runtime supports the required module loading scheme. However, it is not without drawbacks.</p><p>The most apparent downside is a significant increase in code size, given the potential for many, many copies of the same package, all with different versions. An increase in code size can often mean more than just a larger program—it can have a significant impact on performance. Larger programs just don’t fit into CPU caches as easily, and merely having to page a program in and out can significantly slow things down. That’s mostly just a tradeoff, though, since you’re sacrificing performance, not program correctness.</p><p>The more insidious problem (and the one that I see crop up quite a lot in the npm ecosystem without much thought) is how dependency isolation can affect cross-package communication.</p><h2><a name="dependency-isolation-and-values-that-pass-package-boundaries"></a>Dependency isolation and values that pass package boundaries</h2><p>The earlier example of using <code>ramda</code> is a place where npm’s default dependency management scheme really shines, given that Ramda just provides a bunch of plain ol’ functions. Passing these around is totally harmless. In fact, mixing functions from two different versions of Ramda would be totally okay! Unfortunately, not all cases are nearly that simple.</p><p>Consider, for a moment, <code>react</code>. React components are very much <em>not</em> plain old data; they are complex values that can be extended, instantiated, and rendered in a variety of ways. React represents component structure and state using an internal, private format, using a mixture of carefully arranged keys and values and some of the more powerful features of JavaScript’s object system. This internal structure might very well change between React versions, so a React component defined with <code>react@0.3.0</code> likely won’t work quite right with <code>react@15.3.1</code>.</p><p>With that in mind, consider two packages that define their own React components and export them for consumers to use. Looking at their dependency tree, we might see something like this:</p><pre><code>awesome-button +└── react ^0.3.0 + +amazing-modal +└── react ^15.3.1 +</code></pre><p>Given that these two packages use wildly different versions of React, npm would give each of them their own copy of React, as requested, and packages would happily install. However, if you tried to use these components together, they wouldn’t work at all! A newer version of React simply cannot understand an old version’s component, so you would get a (likely confusing) runtime error.</p><p>What went wrong? Well, dependency isolation works great when a package’s dependencies are purely implementation details, never observable from outside of a package. However, as soon as a package’s dependency becomes exposed as part of its <em>interface</em>, dependency isolation is not only subtly wrong, it can cause complete failure at runtime. These are cases when traditional dependency management are much better—they will tell you as soon as you attempt to install two packages that they just don’t work together, rather than waiting for you to figure that out for yourself.</p><p>This might not sound <em>too</em> bad—after all, JavaScript is a very dynamic language, so static guarantees are mostly few and far between, and your tests should catch these problems should they arise—but it can cause unnecessary issues when two packages <em>can</em> theoretically work together fine, but because npm assigned each one its own copy of a particular package (that is, it wasn’t quite smart enough to figure out it could give them both the same copy), things break down.</p><p>Looking outside of npm specifically and considering this model when applied to other languages, it becomes increasingly clear that this won’t do. This blog post was inspired by <a href="https://www.reddit.com/r/haskell/comments/4zc6y3/why_doesnt_cabal_use_a_model_like_that_of_npm/?ref=share&amp;ref_source=link">a Reddit thread discussing the npm model applied to Haskell</a>, and this flaw was touted as a reason why it couldn’t possibly work for such a static language.</p><p>Due to the way the JavaScript ecosystem has evolved, it’s true that most people can often get away with this subtle potential for incorrect behavior without any problems. Specifically, JavaScript tends to rely on duck typing rather than more restrictive checks like <code>instanceof</code>, so objects that satisfy the same protocol will still be compatible, even if their implementations aren’t <em>quite</em> the same. However, npm actually provides a robust solution to this problem that allows package authors to explicitly express these “cross-interface” dependencies.</p><h3><a name="peer-dependencies"></a>Peer dependencies</h3><p>Normally, npm package dependencies are listed under a <code>"dependencies"</code> key in the package’s <code>package.json</code> file. There is, however, another, less-used key called <code>"peerDependencies"</code>, which has the same format as the ordinary dependencies list. The difference shows up in how npm performs dependency resolution: rather than getting its own copy of a peer dependency, a package expects that dependency to be provided by its dependent.</p><p>This effectively means that peer dependencies are effectively resolved using the “traditional” dependency resolution mechanism that tools like Bundler and Cabal use: there must be one canonical version that satisfies everyone’s constraint. Since npm 3, things are a little bit less straightforward (specifically, peer dependencies are not automatically installed unless a dependent package explicitly depends on the peer package itself), but the basic idea is the same. This means that package authors must make a choice for each dependency they install: should it be a normal dependency or a peer dependency?</p><p>This is where I think people tend to get a little lost, even those familiar with the peer dependency mechanism. Fortunately, the answer is relatively simple: is the dependency in question visible in <em>any place</em> in the package’s interface?</p><p>This is sometimes hard to see in JavaScript because the “types” are invisible; that is, they are dynamic and rarely explicitly written out. However, just because the types are dynamic does not mean they are not there at runtime (and in the heads of various programmers), so the rule still holds: if the type of a function in a package’s public interface somehow depends on a dependency, it should be a peer dependency.</p><p>To make this a little more concrete, let’s look at a couple of examples. First off, let’s take a look at some simple cases, starting with some uses of <code>ramda</code>:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">merge</span><span class="p">,</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;ramda&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">withDefaultConfig</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">config</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">merge</span><span class="p">({</span><span class="w"> </span><span class="nx">path</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;.&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">config</span><span class="p">)</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">add5</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">add</span><span class="p">(</span><span class="mf">5</span><span class="p">)</span></code></pre><p>The first example here is pretty obvious: in <code>withDefaultConfig</code>, <code>merge</code> is used purely as an implementation detail, so it’s safe, and it’s not part of the module’s interface. In <code>add5</code>, the example is a little trickier: the result of <code>add(5)</code> is a partially-applied function created by Ramda, so technically, a Ramda-created value is a part of this module’s interface. However, the contract <code>add5</code> has with the outside world is simply that it is a JavaScript function that adds five to its argument, and it doesn’t depend on any Ramda-specific functionality, so <code>ramda</code> can safely be a non-peer dependency.</p><p>Now let’s look at another example using the <code>jpeg</code> image library:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">Jpeg</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;jpeg&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createSquareBuffer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createSquareJpeg</span><span class="p">(</span><span class="nx">size</span><span class="p">).</span><span class="nx">encode</span><span class="p">(</span><span class="nx">cb</span><span class="p">)</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createSquareJpeg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">size</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Jpeg</span><span class="p">(</span><span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="nx">size</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="mf">0</span><span class="p">),</span><span class="w"> </span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="nx">size</span><span class="p">)</span></code></pre><p>In this case, the <code>createSquareBuffer</code> function invokes a callback with an ordinary Node.js <code>Buffer</code> object, so the <code>jpeg</code> library is an implementation detail. If that were the only function exposed by this module, <code>jpeg</code> could safely be a non-peer dependency. However, the <code>createSquareJpeg</code> function violates that rule: it returns a <code>Jpeg</code> object, which is an opaque value with a structure defined exclusively by the <code>jpeg</code> library. Therefore, a package with the above module <em>must</em> list <code>jpeg</code> as a peer dependency.</p><p>This sort of restriction works in reverse, too. For example, consider the following module:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">writeFile</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;fs&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">writeJpeg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span><span class="w"> </span><span class="nx">jpeg</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">jpeg</span><span class="p">.</span><span class="nx">encode</span><span class="p">((</span><span class="nx">image</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span><span class="w"> </span><span class="nx">image</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">))</span></code></pre><p>The above module does not even <em>import</em> the <code>jpeg</code> package, yet it implicitly depends on the <code>encode</code> method of the <code>Jpeg</code> interface. Therefore, despite not even explicitly using it anywhere in the code, a package containing the above module should include <code>jpeg</code> as a peer dependency.</p><p>They key is to carefully consider what contract your modules have with their dependents. If those contracts involve other packages in any way, they should be peer dependencies. If they don’t, they should be ordinary dependencies.</p><h2><a name="applying-the-npm-model-to-other-programming-languages"></a>Applying the npm model to other programming languages</h2><p>The npm model of package management is more complicated than that of other languages, but it provides a real advantage: implementation details are kept as implementation details. In other systems, it’s quite possible to find yourself in “dependency hell”, when you personally know that the version conflict reported by your package manager is not a real problem, but because the package system must pick a single canonical version, there’s no way to make progress without adjusting code in your dependencies. This is extremely frustrating.</p><p>This sort of dependency isolation is not the most advanced form of package management in existence—indeed, far from it—but it’s definitely more powerful than most other mainstream systems out there. Of course, most other languages could not adopt the npm model simply by changing the package manager: having a global package namespace can prevent multiple versions of the same package being installed at a <em>runtime</em> level. The reason npm is able to do what it does is because Node itself supports it.</p><p>That said, the dichotomy between peer and non-peer dependencies is a little confusing, especially to people who aren’t package authors. Figuring out which packages need to go in which group is not always obvious or trivial. Fortunately, other languages might be able to help.</p><p>Returning to Haskell, its strong static type system would potentially allow this distinction to be detected entirely automatically, and Cabal could actually report an error when a package used in an exposed interface was not listed as a peer dependency (much like how it currently prevents importing a transitive dependency without explicitly depending on it). This would allow helper function packages to keep on being implementation details while still maintaining strong interface safety. This would likely take a lot of work to get just right—managing the global nature of typeclass instances would likely make this much more complicated than a naïve approach would accommodate—but it would add a nice layer of flexibility that does not currently exist.</p><p>From the perspective of JavaScript, npm has demonstrated that it can be a capable package manager, despite the monumental burden placed upon it by the ever-growing, ever-changing JS ecosystem. As a package author myself, I would implore other users to carefully consider the peer dependencies feature and work hard to encode their interfaces’ contracts using it—it’s a commonly misunderstood gem of the npm model, and I hope this blog post helped to shed at least a little more light upon it.</p><ol class="footnotes"></ol></article>Climbing the infinite ladder of abstractionhttps://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/https://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/11 Aug 2016<article><p>I started programming in elementary school.</p><p>When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to <a href="https://xkcd.com/974/">solve the general problem</a>. When I learned about programming, I was immediately hooked: it was <em>so easy</em> to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.</p><p>Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of <strong>abstraction</strong>.</p><h2><a name="the-brick-wall-of-inexpressiveness"></a>The brick wall of inexpressiveness</h2><p>When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:</p><pre><code class="pygments"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="p">;</span> +<span class="p">}</span> + +<span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span> +<span class="p">}</span></code></pre><p>This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.</p><p>It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The <em>whole reason</em> I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.</p><p>When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.</p><p>Both of these languages were flawed, as all languages are, but they opened my mind to the idea that <em>programming languages</em> could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.</p><h2><a name="discovering-lisp"></a>Discovering Lisp</h2><p>Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.</p><p>This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?</p><p>The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a <a href="https://github.com/lexi-lambda/libsol">highly dynamic Lisp with a prototypal object system called Sol</a>. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.</p><p>Unfortunately, it was also abysmally slow.</p><p>I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called <a href="http://racket-lang.org">Racket</a>. At about the same time, someone pointed me to a totally different language called <a href="https://www.haskell.org">Haskell</a>. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.</p><h2><a name="a-journey-into-complexity"></a>A journey into complexity</h2><p>Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned <em>so much more</em> since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.</p><p>I’ve also started realizing something else, though: <strong>the languages I’ve settled into are <em>really complicated</em>.</strong></p><p>When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.</p><p>Why?</p><p>Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”</p><p>Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?</p><p>I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.</p><p>Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I <em>still</em> have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?</p><h3><a name="improvement-but-never-mastery"></a>Improvement, but never mastery</h3><p>Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.</p><p>For example I am pretty confident that I <em>get</em> JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques <em>within</em> the language, not entirely new linguistic constructs.</p><p>On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about <code>TypeInType</code>? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do <em>not</em> have a good grasp on all the intricacies of Racket’s macro system.</p><p>This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out <code>DataKinds</code>, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.</p><p>Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is <em>terrible</em>, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.</p><p>Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even <em>more</em> advanced things, and still, none of them truly solve my problems.</p><h2><a name="ultimately-it-all-has-at-least-a-little-value"></a>Ultimately, it all has (at least a little) value</h2><p>It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.</p><p>The scariest part of all is that I think it’s completely worthwhile.</p><p>So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs <em>seem</em> unnecessarily complicated, they <em>seem</em> increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.</p><p>The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.</p><p>Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:</p><pre><code class="pygments"><span class="nl">10</span><span class="w"> </span><span class="kr">INPUT</span><span class="w"> </span><span class="s2">"What is your name: "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span> +<span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">"Hello "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span></code></pre><p>On the other hand, very few would probably understand this one:</p><pre><code class="pygments"><span class="c1">-- | A class for categories.</span> +<span class="c1">-- id and (.) must form a monoid.</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">Category</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="c1">-- | the identity morphism</span> +<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="c1">-- | morphism composition</span> +<span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">c</span></code></pre><p>Yet very few new programs are being written in BASIC, and lots are being written in Haskell.</p><p>Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:</p><pre><code class="pygments"><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composeWithProps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">curry</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">childProps</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">omit</span><span class="p">([</span><span class="s1">&#39;children&#39;</span><span class="p">],</span><span class="w"> </span><span class="nx">childProps</span><span class="p">),</span><span class="w"> </span><span class="nx">childProps</span><span class="p">.</span><span class="nx">children</span><span class="p">));</span> +<span class="w"> </span><span class="c1">// give the composed component a pretty display name for debugging</span> +<span class="w"> </span><span class="nx">composed</span><span class="p">.</span><span class="nx">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`Composed(</span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="si">}</span><span class="sb">, </span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">composed</span><span class="p">;</span> +<span class="p">});</span></code></pre><p>Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.</p><p>That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an <em>extremely</em> productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.</p><h3><a name="reactionary-anti-intellectualism-and-the-search-for-moderation"></a>Reactionary anti-intellectualism and the search for moderation</h3><p>I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. <a href="https://twitter.com/lexi_lambda/status/763111451691134976">I recently tweeted a picture of some rather dense mathematics from a paper I’d read</a>, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.</p><p>Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like <code>map</code> should be banned because they are too confusing and not immediately self-explanatory.</p><p>I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.</p><p>Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern <em>technology</em> possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.</p><p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.</p><p>I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.</p><p>Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.</p><ol class="footnotes"></ol></article>Four months with Haskellhttps://lexi-lambda.github.io/blog/2016/06/12/four-months-with-haskell/https://lexi-lambda.github.io/blog/2016/06/12/four-months-with-haskell/12 Jun 2016<article><p>At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.</p><p>Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an <em>incredible</em> programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.</p><h2><a name="dispelling-some-myths"></a>Dispelling some myths</h2><p>Before moving on and discussing my experiences in depth, I want to take a quick detour to dispel some frequent rumors I hear about why Haskell is at least potentially problematic. These are things I hear a <em>lot</em>, and nothing in my experience so far would lead me to believe these are actually true. Ultimately, I don’t want to spend too much time on these—I think that, for the most part, they are nitpicks that people complain about to avoid understanding the deeper and more insidious problems with the language—but I think it’s important to at least mention them.</p><h3><a name="hiring-haskell-developers-is-not-hard"></a>Hiring Haskell developers is not hard</h3><p>I am on the first Haskell team in my company, and I am among the first Haskell developers we ever hired. Not only were we hiring without much experience with Haskell at all, we explicitly <em>did not</em> want to hire remote. Debate all you like about whether or not permitting remote work is a good idea, but I don’t think anyone would dispute that this constraint makes hiring much harder. We didn’t have any trouble finding a very large stream of qualified applicants, and it definitely seems to have dispelled any fears that we would have trouble finding new candidates in the future.</p><h3><a name="performing-i-o-in-haskell-is-easy"></a>Performing I/O in Haskell is easy</h3><p>Haskell’s purity is a point of real contention, and it’s one of the most frustrating complaints I often hear about Haskell. It is surprisingly common to hear concerns along the lines of “I don’t want to use Haskell because its academic devotion to purity sounds like it would make it very hard to get anything done”. There are very valid reasons to avoid Haskell, but in practice, I/O is not one of them. In fact, I found that isolating I/O in Haskell was much the same as isolating I/O in every other language, which I need to do anyway to permit unit testing.</p><p>...you <em>do</em> write deterministic unit tests for your impure logic, right?</p><h3><a name="working-with-lots-of-monads-is-not-very-difficult"></a>Working with lots of monads is not very difficult</h3><p>The “M word” has ended up being a running joke <em>about</em> Haskell that actually ends up coming up fairly rarely <em>within</em> the Haskell community. To be clear, there is <em>no doubt</em> in my mind that monads make Haskell intimidating and provide a steep learning curve for new users. The proliferation of the joke that monads are impossible to explain, to the point of becoming mythologized, is absolutely indicative of a deeper problem about Haskell’s accessibility. However, once people learn the basics about monads, I’ve found that applying them is just as natural as applying any other programming pattern.</p><p>Monads are used to assist the programmer, not impede them, and they really do pay off in practice. When something has a monadic interface, there’s a decent chance I already know what that interface is going to do, and that makes working with lots of different monads surprisingly easy. Admittedly, I do rely very, very heavily on tooling to help me out here, but with things like mouseover type tooltips, I’ve actually found that working with a variety of different monads and monad transformers is actually quite pleasant, and it makes things very readable!</p><h2><a name="haskell-the-good-parts"></a>Haskell: the good parts</h2><p>With the disclaimers out of the way, I really just want to gush for a little bit. This is not going to be an objective, reasoned survey of why Haskell is good. I am not even really going to touch upon why types are so great and why purity is so wonderful—I’d love to discuss those in depth, but that’s for a different blog post. For now, I just want to touch upon the real surprises, the real things that made me <em>excited</em> about Haskell in ways I didn’t expect. These are the things that my subjective little experience has found fun.</p><h3><a name="language-extensions-are-haskell"></a>Language extensions <em>are</em> Haskell</h3><p>There was a time in my life when I spent a lot of time writing C. There are a lot of compilers for C, and they all implement the language in subtly different but often incompatible ways, especially on different platforms. The only way to maintain a modicum of predictability was to adhere to the standards <em>religiously</em>, even when certain GCC or MSVC extensions seem tantalizingly useful. I was actually bitten a few times by real instances where I figured I’d just use a harmless extension that was implemented everywhere, then found out it worked slightly differently across different compilers in a particular edge case. It was a learning experience.</p><p>It seems that this fear provides a very real distrust for using GHC’s numerous <em>language extensions</em>, and indeed, for a long time, I felt that it was probably an admirable goal to stick to Haskell 98 or Haskell 2010 as closely as possible. Sometimes I chose a slightly more verbose solution that was standard Haskell to avoid turning on a trivial extension that would make the code look a little bit cleaner.</p><p>About a year later, I’m finding that attitude was not only a mistake, but it forced me to often completely miss out on a lot of Haskell’s core value. GHC <em>won</em>, and now GHC and Haskell are basically synonymous. With that in mind, the portability concerns of language extensions are a bit of a non-issue, and turning them on is a very good idea! Some extensions are more than a little dangerous, so they cannot all be turned on without thinking, but the question is absolutely not “Is using language extensions a good idea?” and more “Is using <em>this</em> language extension a good idea?”</p><p>This is important, and I bring it up for a reason: so much of the awesomeness of Haskell is locked behind language extensions. Turning a lot of these on is one of the main things that made me really start to see how incredibly powerful Haskell actually is.</p><h3><a name="phantom-types"></a>Phantom types</h3><p>I’m going to start out by talking about <em>phantom types</em>, which are a pretty simple concept but a powerful one, and they serve as the foundation for a lot of other cool type-level tricks that can make Haskell extremely interesting. The basic idea of a phantom type is simple; it’s a type parameter that isn’t actually used to represent any particular runtime value:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type represents an id for some kind of value, but although the kind of value is specified in the type as the <code>a</code> type parameter, it isn’t actually used anywhere on the data definition—no matter what <code>a</code> is, an <code>Id</code> is just a piece of text. This makes it possible to write functions that operate on specific kinds of ids, and those invariants will be statically checked by the compiler, even though the runtime representation is entirely identical:</p><pre><code class="pygments"><span class="nf">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span></code></pre><p>Using <code>FlexibleInstances</code>, it’s also possible to create different instances for different kinds of ids. For example, it would be possible to have different <code>Show</code> instances depending on the type of id in question.</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"user #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"post #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span></code></pre><p>This provides a simple framework for encoding entirely arbitrary information into the type system, then asking the compiler to actually check assertions about that information. This is made even more powerful with some other extensions, which I’ll talk about shortly.</p><h3><a name="letting-the-compiler-write-code"></a>Letting the compiler write code</h3><p>One of the things I really dislike, more than most things, is boilerplate. A little bit of boilerplate is fine—even necessary at times—but as soon as I start wondering if a code generator would improve things, I think the programming language has pretty much failed me.</p><p>I write a lot of Racket because, in a sense, Racket is the ultimate boilerplate killer: the macro system is a first-class code generator integrated with the rest of the language, and it means that boilerplate is almost never an issue. Of course, that’s not always true: sometimes a bit of boilerplate <em>is</em> still necessary because macros cannot deduce enough information about the program to generate the code entirely on their own, and in Haskell, some of that information is actually present in the type system.</p><p>This leads to two absolutely incredible extensions, both of which are simple and related, but which actually <em>completely change</em> how I approach problems when programming. These two extensions are <code>GeneralizedNewtypeDeriving</code> and <code>StandaloneDeriving</code>.</p><h4><a name="newtypes-and-type-safety"></a>Newtypes and type safety</h4><p>The basic idea is that “newtypes” are just simple wrapper types in Haskell. This turns out to be extremely important when trying to find the value of Haskell because they allow you to harden type safety by specializing types to <em>your</em> domain. For example, consider a type representing a user’s name:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type is extremely simple, and in fact isn’t even at all different from a simple <code>Text</code> value with respect to its representation, since all combinations of unicode characters are allowed in a name. Therefore, what’s the point of a separate type? Well, this allows Haskell to introduce actual compilation failures when two different kinds of textual data are mixed. This is not a new idea, and even in languages that don’t support this sort of thing, Joel Spolsky’s old blog post <a href="http://www.joelonsoftware.com/articles/Wrong.html">Making Wrong Code Look Wrong</a> describes how it can be done by convention. Still, almost every modern language makes this possible: in C, it would be a single-member <code>struct</code>, in class-based OO languages, it would be a single-member class... this is not a complicated idea.</p><p>The difference lies in its usage. In other languages, this strategy is actually not very frequently employed for the simple reason that it is almost always extremely annoying. You are forced to do tons of wrapping/unwrapping, and at that point it isn’t really clear if you’re even getting all that much value out of the distinction when your first solution to a type mismatch is wrapping or unwrapping the value without a second thought. In Haskell, however, this can be heavily mitigated by asking the compiler to <em>automatically derive typeclass implementations</em>, which allow the unwrapping/wrapping to effectively happen implicitly for a constrained set of operations.</p><h4><a name="using-generalizednewtypederiving"></a>Using <code>GeneralizedNewtypeDeriving</code></h4><p>Consider the <code>Name</code> type once again, but this time, let’s derive a class:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">IsString</span><span class="p">)</span></code></pre><p>The <code>IsString</code> typeclass in Haskell allows custom types to automatically be created from string literals. It is <em>not</em> handled specially by Haskell’s <code>deriving</code> mechanism. Since <code>Text</code> implements <code>IsString</code>, an instance will be generated that simply defers to the underlying type, automatically generating the code to wrap the result up in a <code>Name</code> box at the end. This means that code like this will now just magically work:</p><pre><code class="pygments"><span class="nf">name</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Name</span> +<span class="nf">name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa P. Hacker"</span></code></pre><p>No boilerplate needs to be written! This is a neat trick, but it actually turns out to be far more useful than that simple example in practice. What really makes this functionality shine is when you want to derive <em>some</em> kinds of functionality but disallow some others. For example, using the <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a> package, it’s possible to do something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">ToText</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>This creates an opaque <code>Id</code> type, but it automatically generates conversions <em>to</em> textual formats. However, it does <em>not</em> automatically create <code>FromText</code> or <code>FromJSON</code> instances, which would be dangerous because decoding <code>Id</code>s can potentially fail. It’s then possible to write out those instances manually to preserve a type safety:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">FromText</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fromText</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="n">isValidId</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="kr">then</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">String</span><span class="w"> </span><span class="n">val</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">fromText</span><span class="w"> </span><span class="n">val</span><span class="p">)</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span></code></pre><h4><a name="using-standalonederiving"></a>Using <code>StandaloneDeriving</code></h4><p>The ordinary <code>deriving</code> mechanism is extremely useful, especially when paired with the above, but sometimes it is desirable to have a little bit more flexibility. In these cases, <code>StandaloneDeriving</code> can help.</p><p>Take the <code>Id</code> example again: it has a phantom type, and simply adding something like <code>deriving (ToText)</code> with derive <code>ToText</code> instances for <em>all</em> kinds of ids. It is potentially useful, however, to derive instances for more specific id types. Using standalone <code>deriving</code> constructs permits this sort of flexibility.</p><pre><code class="pygments"><span class="kr">deriving</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toText</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">postIdToText</span></code></pre><p>This is an example where GHC language extensions end up becoming significantly more than the sum of their parts, which seems to be a fairly frequent realization. The <code>StandaloneDeriving</code> mechanism is a little bit useful without <code>GeneralizedNewtypeDeriving</code>, but when combined, they are incredibly powerful tools for getting a very fine-grained kind of type safety <em>without</em> writing any boilerplate.</p><h3><a name="datakinds-are-super-cool-with-caveats"></a>DataKinds are super cool, with caveats</h3><p>Phantom types are quite wonderful, but they can only encode <em>types</em>, not arbitrary data. That’s where <code>DataKinds</code> and <code>KindSignatures</code> come in: they allow lifting arbitrary datatypes to the type level so that things that would normally be purely runtime values can be used at compile-time as well.</p><p>The way this works is pretty simple—when you define a datatype, you also define a “datakind”:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Registered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Anonymous</span></code></pre><p>Normally, the above declaration declares a <em>type</em>, <code>RegistrationStatus</code>, and two <em>data constructors</em>, <code>Registered</code> and <code>Anonymous</code>. With <code>DataKinds</code>, it also defines a <em>kind</em>, <code>RegistrationStatus</code>, and two <em>type constructors</em>, <code>Registered</code> and <code>Anonymous.</code></p><p>If that’s confusing, the way to understand that is to realize there is a sort of natural ordering here: types describe values, and kinds describe types. Therefore, turning on <code>DataKinds</code> “lifts” each definition by a single level, so types become kinds and values become types. This permits using these things at the type level:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>In this example, <code>UserId</code> still has a single phantom type variable, <code>s</code>, but this time it is constrained to the <code>RegistrationStatus</code> kind. Therefore, it can <em>only</em> be <code>Registered</code> or <code>Anonymous</code>. This cooperates well with the aforementioned <code>StandaloneDeriving</code> mechanism, and it mostly provides a convenient way to constrain type variables to custom kinds.</p><p>In general, <code>DataKinds</code> is a much more powerful extension, allowing things like type-level natural numbers or strings, which can be used to perform actual type-level computation (especially in combination with <code>TypeFamilies</code>) or a sort of metaprogramming. In some cases, they can even be used to implement things emulating things you can do with dependent types.</p><p>I think <code>DataKinds</code> are a very cool Haskell extension, but there are a couple caveats. One of the main ones is how new kinds are defined: <code>DataKinds</code> “hijacks” the existing datatype declaration syntax by making every single datatype declaration define a type <em>and</em> a kind. This is a little confusing, and it would be nice if a different syntax was used so that each could be defined independently.</p><p>Similarly, it seems that a lot of work is being done to allow using runtime values at the type level, but I wonder if people will ever need to use, say, runtime values at the <em>kind</em> level. This immediately evokes thoughts of Racket’s phase-based macro system, and I wonder if some of this duplication would be unnecessary with something similar.</p><p>Food for thought, but overall, <code>DataKinds</code> are a very nice addition to help with precisely and specifically typing particular problems.</p><h3><a name="typeclasses-can-emulate-effects"></a>Typeclasses can emulate effects</h3><p>This is something that I’ve found interesting in my time writing Haskell because I have <em>no idea</em> if it’s idiomatic or not, but it seems pretty powerful. The initial motivator for this idea was figuring out how to test our code without constantly dropping into <code>IO</code>.</p><p>More generally, we wanted to be able to unit test by “mocking” out collaborators, as it would be described in object oriented programming. I was always semi-distrustful of mocking, and indeed, it seems likely that it is heavily abused in certain circles, but I’ve come to appreciate the need that sometimes it is important to stub things out, <em>even in pure code</em>.</p><p>As an example, consider some code that needs access to the current time. This is something that would normally require <code>IO</code>, but we likely want to be able to use the value in a pure context without “infecting” the entire program with <code>IO</code> types. In Haskell, I have generally seen three ways of handling this sort of thing:</p><ol><li><p>Just inject the required values into the function and produce them “higher up” where I/O is okay. If threading the value around becomes too burdensome, use a Reader monad.</p></li><li><p>Use a free monad or similar to create a pure DSL of sorts, then write interpreters for various implementations, one of which uses <code>IO</code>.</p></li><li><p>Create custom monadic typeclasses that provide interfaces to the functionality you want to perform, then create instances, one of which is an instance over <code>IO</code>.</p></li></ol><p>This last approach seems to be less common in Haskell, but it’s the approach we took, and it seems to work out remarkably well. Returning to the need to get the current time, we could pretty easily write such a typeclass to encode that need:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">UTCTime</span></code></pre><p>Now we can write functions that use the current time:</p><pre><code class="pygments"><span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span><span class="p">)</span></code></pre><p>Now, we can write instances for <code>CurrentTime</code> that will allow us to run the same code in different contexts:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runAppM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">runTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">runTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftIO</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Time</span><span class="o">.</span><span class="kt">Clock</span><span class="o">.</span><span class="n">getCurrentTime</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">posixSecondsToUTCTime</span><span class="w"> </span><span class="mi">0</span></code></pre><p>Where this really starts to shine is when adding additional effects. For example, the above token validation function might also need information about some kind of secret used for signing. Under this model, it’s just another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getTokenSecret</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Secret</span> + +<span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">secret</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getTokenSecret</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span> +<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">verifySignature</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="n">secret</span><span class="p">)</span></code></pre><p>Of course, so far all of these functions have been extremely simple, and we’ve basically been using them as a glorified reader monad. In practice, though, we use this pattern for lots more than just retrieving values. For example, we might have a typeclass for database interactions:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> +<span class="w"> </span><span class="n">insertUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">PersistenceError</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">))</span></code></pre><p>With all of this done, it becomes incredibly easy to see which functions are using which effects:</p><pre><code class="pygments"><span class="nf">postUsers</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">postUsers</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">getHealthcheck</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">getHealthcheck</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>There’s no need to perform any lifting, and this all seems to scale quite nicely. We’ve written some additional utilities to help write tests against functions using these kinds of monadic interfaces, and even though there’s a little bit of annoying boilerplate in a few spots, overall it seems to work quite elegantly.</p><p>I’m not entirely sure how common this is in the Haskell community, but it’s certainly pretty neat how easy it is to get nearly all of the benefits of effect types in other languages simply by composing some of Haskell’s simplest features.</p><h3><a name="atom-s-ide-haskell-tooling-is-invaluable"></a>Atom’s ide-haskell tooling is invaluable</h3><p>Alright, so, confession time: I don’t use Emacs.</p><p>I know, I know, how is that possible? I write Lisp, after all. Well, honestly, I tried picking it up a number of times, but none of those times did I get far enough to ditch my other tools. For Racket work, I use DrRacket, but for almost everything else, I use Atom.</p><p>Atom has a lot of flaws, but it’s also pretty amazing in places, and I absolutely <em>love</em> the Haskell tooling written by the wonderful <a href="https://github.com/atom-haskell">atom-haskell</a> folks. I use it constantly, and even though it doesn’t always work perfectly, it works pretty well. When it has problems, I’ve at least figured out how to get it working correctly.</p><p>This is probably hard to really explain without seeing it for yourself, but I’ve found that I basically <em>depend</em> on this sort of tooling to be fully productive in Haskell, and I have no problem admitting that. The ability to get instant feedback about type errors tied to visual source locations, to be able to directly manipulate the source by selecting expressions and getting type information, and even the option to get inline linter suggestions means I spend a lot less time glancing at the terminal, and even less time in the REPL.</p><p>The tooling is far from perfect, and it leaves a lot to be desired in places (the idea of using that static information for automated, project-wide refactoring <em>a la</em> Java is tantalizing), but most of those things are ideas of what amazing things could be, not broken or missing essentials. I am pretty satisfied with ide-haskell right now, and I can only hope it continues to get better and better.</p><h2><a name="frustrations-drawbacks-and-pain-points"></a>Frustrations, drawbacks, and pain points</h2><p>Haskell is not perfect. In fact, far from it. There is a vast array of little annoyances that I have with the language, as is the case with any language. Still, there are a few overarching problems that I would really like to at least mention. These are the biggest sources of frustration for me so far.</p><h3><a name="purity-failure-and-exception-handling"></a>Purity, failure, and exception-handling</h3><p>One of Haskell’s defining features is its purity—I don’t think many would disagree with that. Some people consider it a drawback, others consider it one of its greatest boons. Personally, I like it a lot, and I think one of the best parts about it is how it requires the programmer to be incredibly deliberate about failure.</p><p>In many languages, when looking up a value from a container where the key doesn’t exist, there are really two ways to go about expressing this failure:</p><ol><li><p>Throw an exception.</p></li><li><p>Return <code>null</code>.</p></li></ol><p>The former is scary because it means <em>any</em> call to any function can make the entire program blow up, and it’s often impossible to know which functions even have the potential to throw. This creates a certain kind of non-local control flow that can sometimes cause a lot of unpredictability. The second option is much the same, especially when any value in a program might be <code>null</code>; it just defers the failure.</p><p>In languages with option types, this is somewhat mitigated. Java now has option types, too, but they are still frequently cumbersome to use because there is nothing like monads to use to simply chain operations together. Haskell, in comparison, has an incredible complement of tools to simply handle errors without a whole lot of burden on the programmer, and I have found that, in practice, this is <em>actually helpful</em> and I really do write better error-handling code.</p><h4><a name="first-the-good-parts"></a>First, the good parts</h4><p>I have seen a comparison drawn between throwing checked exceptions and returning <code>Maybe</code> or <code>Either</code> types, but in practice the difference is massive. Handling checked exceptions is a monotonous chore because they are not first-class values, they are actually entirely separate linguistic constructs. Consider a library that throws a <code>LibraryException</code>, and you want to wrap that library and convert those exceptions to <code>ApplicationException</code>s. Well, have fun writing this code dozens of times:</p><pre><code class="pygments"><span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomething</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span> + +<span class="c1">// ...</span> + +<span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomethingElse</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span></code></pre><p>In Haskell, failure is just represented by first-class values, and it’s totally possible to write helper functions to abstract over that kind of boilerplate:</p><pre><code class="pygments"><span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ApplicationError</span> +<span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">ApplicationError</span><span class="w"> </span><span class="n">a</span> +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapLeft</span><span class="w"> </span><span class="n">libraryToApplication</span></code></pre><p>Now, that same boilerplate-y code becomes nearly invisible:</p><pre><code class="pygments"><span class="nf">x</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomething</span> + +<span class="c1">-- ...</span> + +<span class="nf">y</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomethingElse</span></code></pre><p>This might not <em>seem</em> like much, but it really cuts down on the amount of visual noise, which ends up making all the difference. Boilerplate incurs a cost much bigger than simply taking the time to type it all out (though that’s important, too): the cognitive overhead of parsing which parts of a program are boilerplate has a significant impact on readability.</p><h4><a name="so-what-s-the-problem"></a>So what’s the problem?</h4><p>If error handling is so great in Haskell, then why am I putting it under the complaints section? Well, it turns out that not everyone seems to think it’s as great as I make it out to be because people seem to keep writing Haskell APIs that throw exceptions!</p><p>Despite what some purists would have you believe, Haskell has exceptions, and they are not uncommon. Lots of things can throw exceptions, some of which are probably reasonable. Failing to connect to a database is a pretty catastrophic error, so it seems fair that it would throw. On the other hand, inserting a duplicate record is pretty normal operation, so it seems like that should <em>not</em> throw.</p><p>I mostly treat exceptions in Haskell as unrecoverable catastrophes. If I throw an error in <em>my</em> code, I do not intend to catch it. That means something horrible happened, and I just want that horribleness to show up in a log somewhere so I can fix the problem. If I care about failure, there are better ways to handle that failure gracefully.</p><p>It’s also probably worth noting that exceptions in Haskell can be thrown from anywhere, even pure code, but can only be <em>caught</em> within the <code>IO</code> monad. This is especially scary, but I’ve seen it happen in actual libraries out in the wild, even ones that the entire Haskell ecosystem is built on. One of the crowning examples of this is the <code>text</code> package, which provides a function called <code>decodeUtf8</code> to convert bytestrings into text. Its type is very simple:</p><pre><code class="pygments"><span class="nf">decodeUtf8</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ByteString</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>But wait, what if the bytestring is not actually a valid UTF-8 string?</p><p>Boom. There goes the application.</p><p>Okay, okay, well, at least the <code>text</code> package provides another function, this one called <code>decodeUtf8'</code>, which returns an <code>Either</code>. This is good, and I’ve trained myself to only ever use <code>decodeUtf8'</code>, but it still has some pretty significant problems:</p><ul><li><p>The <em>safe</em> version of this function is the “prime” version, rather than the other way around, which encourages people to use the unsafe one. Ideally, the unsafe one should be explicitly labeled as such... maybe call it <code>unsafeDecodeUtf8</code>?</p></li><li><p>This is not a hypothetical problem. When using a Haskell JWT library, we found a function that converts a string into a JWT. Since not all strings are JWTs, the library intelligently returns a <code>Maybe</code>. Therefore, we figured we were safe.</p><p>A couple weeks later, we found that providing this function with invalid data was returning HTTP 500 errors. Why? Our error handling was meticulous! Well, the answer was a <code>decodeUtf8</code> call, hidden inside of the JWT library. This is especially egregious, given that the API it exposed returned a <code>Maybe</code> anyway! It would have been trivial to use the safe version there, instead, but the poor, misleading name led the library developer to overlook the bug lurking in the otherwise innocuous function.</p><p>Even worse, this function was totally pure, and we used it in pure code, so we could not simply wrap the function and catch the exception. We had two options: use <code>unsafePerformIO</code> (yuck!) or perform a check before handing the data to the buggy function. We chose the latter, but in some cases, I imagine that could be too difficult to do in order to make it feasible.</p></li></ul><p>The point I’m trying to make is that this is a real problem, and it seems to me that throwing exceptions invalidates one of the primary advantages of Haskell. It disappointed me to realize that a significant amount of code written by FP Complete, one of the primary authors of some of the most important “modern Haskell” code in existence (including Stack), seem to very frequently expose APIs that will throw.</p><p>I’m not sure how much of this stems from a fundamental divide in the Haskell ecosystem and how much it is simply due to Michael Snoyman’s coding style, given that he is the primary author of a number of these tools and libraries that seem very eager to throw exceptions. As just one example of a real situation in which we were surprised by this behavior, we used Snoyman’s http-client library and found that it mysteriously throws upon nearly <em>any</em> failure state:</p><blockquote><p>A note on exceptions: for the most part, all actions that perform I/O should be assumed to throw an <code>HttpException</code> in the event of some problem, and all pure functions will be total. For example, <code>withResponse</code>, <code>httpLbs</code>, and <code>BodyReader</code> can all throw exceptions.</p></blockquote><p>This doesn’t seem entirely unreasonable—after all, isn’t a failure to negotiate TLS fairly catastrophic?—until you consider our use case. We needed to make a subrequest during the extent of another HTTP request to our server, and if that subrequest fails, we absolutely need to handle that failure gracefully. Of course, this is not <em>terrible</em> given that we are in <code>IO</code> so we can actually catch these exceptions, but since this behavior was only noted in a single aside at the top of the documentation, we didn’t realize we were forgetting error handling until far too late and requests were silently failing.</p><p>Exceptions seem to devalue one of the most powerful concepts in Haskell: if I don’t consider all the possibilities, my code <em>does not compile</em>. In practice, when working with APIs that properly encode these possibilities into the type system, this value proposition seems to be real. I really do find myself writing code that works correctly as soon as it compiles. It’s almost magical.</p><p>Using exceptions throws that all out the window, and I wish the Haskell ecosystem was generally more cautious about when to use them.</p><h3><a name="the-string-problem"></a>The String problem</h3><p>I sort of alluded to this a tiny bit in the last section, and that is probably indicative of how bad this issue is. I’m just going to be blunt: <strong>In Haskell, strings suck.</strong></p><p>This is always a bit of an amusing point whenever it is discussed because of how silly it seems. Haskell is a research language with a cutting-edge type system and some of the fanciest features of any language in existence. When everyday programming might use things like “profunctors”, “injective type families”, and “generalized algebraic datatypes”, you would think that dealing with <em>strings</em> would be a well-solved problem.</p><p>But it isn’t. Haskell libraries frequently use not one, not two, but <em><strong>five</strong></em> kinds of strings. Let’s list them off, shall we?</p><ul><li><p>First off, there’s the built-in <code>String</code> type, which is actually an alias for the <code>[Char]</code> type. For those not intimately familiar with Haskell, that’s a <em>linked list of characters</em>. As <a href="http://www.stephendiehl.com/">Stephen Diehl</a> recently put it in <a href="http://www.stephendiehl.com/posts/strings.html">a blog post describing the disaster that is Haskell string types</a>:</p><blockquote><p>This is not only a bad representation, it’s quite possibly the least efficient (non-contrived) representation of text data possible and has horrible performance in both time and space. <em>And it’s used everywhere in Haskell.</em></p></blockquote><p>The point is, it’s really bad. This type is not a useful representation for textual data in practical applications.</p></li><li><p>Moving on, we have a fairly decent type, <code>Text</code>, which comes from <code>Data.Text</code> in the <code>text</code> package. This is a decent representation of text, and it’s probably the one that everything should use. Well, maybe. Because <code>Text</code> comes in two varieties: lazy and strict. Nobody seems to agree on which of those two should be used, though, and they are totally incompatible types: functions that work with one kind of text won’t work with the other. You have to manually convert between them.</p></li><li><p>Finally, we have <code>ByteString</code>, which is horribly misnamed because it really isn’t a string at all, at least not in the textual sense. A better name for this type would have simply been <code>Bytes</code>, which sounds a lot scarier. And that would be good, because data typed as a <code>ByteString</code> is as close as you can get in Haskell to not assigning a type at all: a bytestring holds arbitrary bytes without assigning them any meaning whatsoever!</p><p>Or at least, that’s the intention. The trouble is that people <em>don’t</em> treat bytestrings like that—they just use them to toss pieces of text around, even when those pieces of text have a well-defined encoding and represent textual data. This leads to the <code>decodeUtf8</code> problem mentioned above, but it’s bigger than that because it often ends up with some poor APIs that assign some interpretation to <code>ByteString</code> data without assigning it a different type.</p><p>Again, this is throwing away so much of Haskell’s safety. It would be like using <code>Int</code> to keep track of boolean data (“just use 0 and 1!”) or using empty and singleton lists instead of using <code>Maybe</code>. When you use the precise type, you encode invariants and contracts into statically-checked assertions, but when you use general types like <code>ByteString</code>, you give that up.</p><p>Oh, and did I mention that <code>ByteString</code>s also come in incompatible lazy and strict versions, too?</p></li></ul><p>So, obviously, the answer is to just stop using the bad types and to just use (one kind of) <code>Text</code> everywhere. Great! Except that the other types are totally inescapable. The entire standard library uses <code>String</code> exclusively—after all, <code>text</code> is a separate package—and small libraries often use <code>String</code> instead of <code>text</code> because they have no need to bring in the dependency. Of course, this just means every real application pays the performance hit of converting between all these different kinds of strings.</p><p>Similarly, those that <em>do</em> use <code>Text</code> often use different kinds of text, so code ends up littered with <code>fromStrict</code> or <code>toStrict</code> coercions, which (again) have a cost. I’ve already ranted enough about <code>ByteString</code>, but basically, if you’re using <code>ByteString</code> in your API to pass around data that is semantically text, you are causing me pain. Please stop.</p><p>It seems that the way <code>Data.Text</code> probably <em>should</em> have been designed was by making <code>Text</code> a typeclass, then making the lazy and strict implementations instances of that typeclass. Still, the fact that both of them exist would always cause problems. I’m actually unsure which one is the “correct” choice—I don’t know enough about how the two perform in practice—but it seems likely that picking <em>either</em> one would be a performance improvement over the current system, which is constantly spending time converting between the two.</p><p>This issue has been ranted about plenty, so I won’t ramble on, but if you’re designing new libraries, please, <em>please</em> use <code>Text</code>. Your users will thank you.</p><h3><a name="documentation-is-nearly-worthless"></a>Documentation is nearly worthless</h3><p>Finally, let’s talk about documentation.</p><p>One of my favorite programming languages is Racket. Racket has a documentation tool called Scribble. Scribble is special because it is a totally separate domain-specific language for writing documentation, and it makes it fun and easy to write good explanations. There are even forms for typesetting automatically-rendered examples that look like a REPL. If the examples ever break or become incorrect, the docs don’t even compile.</p><p>All of the Racket core library documentation makes sure to set a good example about what good documentation should look like. The vast majority of the documentation is paragraphs of prose and simple but practical examples. There are also type signatures (in the form of contracts), and those are super important, but they are so effective because of how the prose explains what each function does, when to use it, <em>why</em> you’d use it, and <em>why you wouldn’t use it</em>.</p><p>Everything is cross-referenced automatically. The documentation is completely searchable locally out of the box. As soon as you install a package, its docs are automatically indexed. User-written libraries tend to have pretty good docs, too, because the standard libraries set such a good example <em>and</em> because the tools are so fantastic. Racket docs are really nice, and they’re so good they actually make things like Stack Overflow or even Google mostly irrelevant. It’s all there in the manual.</p><p>Haskell documentation is the opposite of everything I just said.</p><ul><li><p>The core libraries are poorly documented. Most functions include a sentence of description, and almost none include examples. At their worst, the descriptions simply restate the type signature.</p></li><li><p>Third-party libraries’ documentation is even worse, going frequently completely undocumented and actually only including type signatures and nothing else.</p></li><li><p>Haddock is an incredibly user-hostile tool for writing anything other than tiny snippets of documentation and is not very good at supporting prose. Notably, Haddock’s documentation is not generated using Haddock (and it still manages to be almost unusable). Forcing all documentation into inline comments makes users unlikely to write much explanation, and there is no ability for abstraction.</p></li><li><p>Reading documentation locally is very difficult because there is no easy way to open documentation for a particular package in a web browser, and it’s <em>certainly</em> not searchable. This is especially ridiculous given that Hoogle exists, which is one of best ways to search API docs in existence. There should be a <code>stack hoogle</code> command that just opens a Hoogle page for all locally-installed packages and Just Works, but there isn’t.</p></li><li><p>Most valuable information exists outside of documentation, so Google becomes a go-to immediately after a quick glance at the docs, and information is spread across blog posts, mailing lists, and obscure reddit posts.</p></li></ul><p>This is a problem that cannot be fixed by just making Haddock better, nor can it be fixed simply by improving the existing standard library documentation. There is a fundamental problem with Haskell documentation (which, to be completely fair, is not unique to Haskell), which is that its tools do not support anything more than API docs.</p><p>Good documentation is so much more than “here’s what this function does”; it’s about guides and tutorials and case studies and common pitfalls. <a href="http://docs.racket-lang.org/lens/lens-guide.html">This is documentation for someone new to lenses.</a> <a href="https://hackage.haskell.org/package/lens#readme">This is not.</a> Take note of the difference.</p><h2><a name="conclusion-and-other-thoughts"></a>Conclusion and other thoughts</h2><p>Haskell is an incredible programming platform, and indeed, it is sometimes mind-boggling how complete it is. It also has a lot of rough edges, sometimes in places that feel like they need a lot more care, or perhaps they’re even simply unfinished.</p><p>I could spend weeks writing about all the things I really like or dislike about the language, discussing in fine detail all the things that have made me excited or all the little bits that have made me want to tear my hair out. Heck, I could probably spend a month writing about strings alone. That’s not the point, though... I took a risk with Haskell, and it’s paid off. I’m not yet sure exactly how I feel about it, or when I would chose it relative to other tools, but it is currently very high on my list of favorite technologies.</p><p>I did not come to Haskell with a distaste for static typing, despite the fact that I write so much Racket, a dynamically typed language (by default, at least). I don’t really use Typed Racket, and despite my love for Haskell and its type system, I am not sure I will use much more of it than I did before. Haskell and Racket are very different languages, which is justified in some places and probably sort of circumstantial in others.</p><p>The future of Haskell seems bright, and a lot of the changes in the just-released GHC 8 are extremely exciting. I did not list records as a pain point because the changes in GHC 8 appear to make them a <em>lot</em> more palatable, although whether or not they solve that problem completely remains to be seen. I will absolutely continue to write Haskell and push it to its limits where I can, and hopefully try and take as much as I can from it along the way.</p><ol class="footnotes"></ol></article>Simple, safe multimethods in Rackethttps://lexi-lambda.github.io/blog/2016/02/18/simple-safe-multimethods-in-racket/https://lexi-lambda.github.io/blog/2016/02/18/simple-safe-multimethods-in-racket/18 Feb 2016<article><p>Racket ships with <code>racket/generic</code>, a system for defining <em>generic methods</em>, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports <em>single dispatch</em>. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.</p><h2><a name="motivating-multiple-dispatch"></a>Motivating multiple dispatch</h2><p>What is multiple dispatch and why is it necessary? Well, in most cases, it <em>isn’t</em> necessary at all. <a href="http://dl.acm.org/citation.cfm?doid=1449764.1449808">It has been shown that multiple dispatch is much rarer than single dispatch in practice.</a> However, when actually needed, having multiple dispatch in the toolbox is a valuable asset.</p><p>A classic example of multiple dispatch is multiplication over both scalars and vectors. Ideally, all of the following operations should work:</p><pre><code>2 × 3 = 6 +2 × ⟨3, 4⟩ = ⟨6, 8⟩ +⟨3, 4⟩ × 2 = ⟨6, 8⟩ +</code></pre><p>In practice, most languages do not support such flexible dispatch rules without fairly complicated branching constructs to handle each permutation of input types. Furthermore, since most languages only support single dispatch (such as most object-oriented languages), it is nearly impossible to add support for a new combination of types to an existing method.</p><p>To illustrate the above, even if a language supported operator overloading <em>and</em> it included a <code>Vector</code> class that overloaded multiplication to properly work with numbers and vectors, it might not implement matrix multiplication. If a user defines a <code>Matrix</code> class, they may overload <em>its</em> multiplication to support numbers, vectors, and matrices, but it is impossible to extend the multiplication implementation for the <code>Vector</code> class. That method is now completely set in stone, unless it is edited directly (and the programmer may not have access to <code>Vector</code>’s implementation).</p><p>Multiple dispatch solves all of these problems. Rather than specify implementations of functions for singular types, it is possible to specify implementations for sets of types. In the above example, a programmer would be able to define a new function that operates on <code>Vector</code> and <code>Matrix</code> arguments. Since each definition does not “belong” to any given type, extending this set of operations is trivial.</p><h2><a name="multiple-dispatch-in-racket"></a>Multiple dispatch in Racket</h2><p>This blog post is somewhat long and technical, so before proceeding any further, I want to show some real code that actually works so you can get a feel for what I’m talking about. As a proof-of-concept, I have created <a href="https://github.com/lexi-lambda/racket-multimethod">a very simple implementation of multiple dispatch in Racket</a>. The above example would look like this in Racket using my module:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Pardon the somewhat clunky syntax, but the functionality is there. Using the above code works as expected:</p><pre><code>&gt; (mul (num 2) (num 3)) +(num 6) +&gt; (mul (num 2) (vec '(3 4))) +(vec '(6 8)) +&gt; (mul (vec '(3 4)) (num 2)) +(vec '(6 8)) +</code></pre><p>Making the above snippet work is not particularly hard. In fact, it’s likely that most competent Racketeers could do it without much thought. However, there’s a tiny bit more going on behind the scenes than it may seem.</p><h2><a name="the-problem-with-multiple-dispatch"></a>The problem with multiple dispatch</h2><p>The single-dispatch design limitation of <code>racket/generic</code> comes directly from a desire to avoid what has been described as “spooky action at a distance”, a problem that is prevalent in many systems that support methods with multiple dispatch (aka <em>multimethods</em>). Specifically, the issue arises when new method implementations are defined for existing datatypes, which can have far-reaching effects throughout a program because the method table is global state. Both CLOS and Clojure suffer from this shortcoming.</p><p>Interestingly, Haskell with multi-parameter typeclasses (a nonstandard but highly useful extension) makes it quite trivial to create constructs similar to multiple dispatch (though the overload resolution is done at compile-time). The similarities are significant: Haskell <em>also</em> suffers from the possibility of a certain sort of “spooky action”. However, Haskell’s static typing and resolution allows the compiler to catch these potential issues, known as “orphan instances”, at compile time. Even though Racket does not support the same sort of static typing, the same idea can be used to keep multiple dispatch safe using the macro system.</p><h2><a name="safe-dynamically-typed-multiple-dispatch"></a>Safe, dynamically-typed multiple dispatch</h2><p>In order to make multiple dispatch safe, we first need to determine exactly what is unsafe. Haskell has rules for determining what constitutes an “orphan instance”, and these rules are equally applicable for determining dangerous multimethod implementations. Specifically, a definition can be considered unsafe if <em>both</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented was declared in a different module from the implementation.</p></li><li><p><em>All</em> of the types used for dispatch in the multimethod instance were declared in a different module from the implementation.</p></li></ol><p>Conversely, a multimethod implementation is safe if <em>either</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></li><li><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></li></ol><p>Why do these two rules provide a strong enough guarantee to eliminate the dangers created by global state? Well, to understand that, we need to understand what can go wrong if these rules are ignored.</p><h3><a name="multimethods-and-dangerous-instances"></a>Multimethods and dangerous instances</h3><p>What exactly is this dangerous-sounding “spooky action”, and what causes it? Well, the trouble stems from the side-effectful nature of multimethod instance definitions. Consider the Racket module from earlier, which defines multiplication instances for scalars and vectors:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Note that there is not actually a <code>(mul vec vec)</code> implementation. This is intentional: there are <em>two</em> ways to take the product of two vectors, so no default implementation is provided. However, it is possible that another module might desire an instance for <code>mul</code> that takes the dot product, and the programmer might write the following definition:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">foldl</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">y</span><span class="p">)))))</span></code></pre><p>However, there is something fishy about the above definition: it doesn’t need to be exported with <code>provide</code> to work! Since instances don’t create new bindings, they only add dispatch options, they don’t ever need to <code>provide</code> anything. This is problematic, though: it means that a program could continue to happily compile <em>even if</em> the module containing the dot product instance was never loaded with <code>require</code>, but an attempt to multiply two vectors would fail at runtime, claiming that there was no <code>(mul vec vec)</code> implementation. This drastic change of behavior violates Racket programmers’ assumptions about the guarantees made by modules (<code>require</code> should not cause any side-effects if the module’s bindings are not used).</p><p>Of course, while this seems potentially unexpected, it is workable: just be careful to <code>require</code> modules containing instances. Unfortunately, it gets much worse—what if a different library defines <em>its own</em> <code>(mul vec vec)</code> instance? What if that instance takes the cross product instead? That library may function entirely properly on its own, but when loaded alongside the program that defines a dot product instance, it is impossible to determine which instance should be used where. Because <code>define-instance</code> operates by modifying the aforementioned global state, the implementations clash, and the two systems <em>cannot</em> continue to operate together as written.</p><p>This is pretty bad. Defining extra instances is a reasonable use-case for multiple dispatch, but if these instances can break <em>third-party code</em>, how can they be trusted? This sort of problem can make multiple dispatch difficult to reason about and even more difficult to trust.</p><h3><a name="what-determines-safety"></a>What determines safety?</h3><p>With those problems in mind, we can turn back to the two rules for <em>safe</em> multiple dispatch. How do they prevent the above issues? Well, let’s take them one at a time.</p><p>Remember that an instance can be unequivocally determined to be safe if either of the two conditions are true, so we can consider them entirely independently. The first one is simple—an instance is safe if the following condition holds:</p><blockquote><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></blockquote><p>This one is pretty obvious. It is impossible to create a “bad” instance of a method declared in the same module because it is impossible to import the method without also bringing in the instance. Furthermore, a conflicting instance cannot be defined at the place where the types themselves are defined because that would require a circular module dependency, which Racket does not permit.</p><p>With the above explanation in mind, the second condition should make sense, too:</p><blockquote><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></blockquote><p>The same argument for the first point holds for the second, but with the parties swapped. Again, it is impossible to use the instance without somehow requiring the module that defines the datatype itself, so the instance would always be required, anyway. The most interesting aspect of this condition is that it demonstrates that instances can be defined for existing datatypes (that are defined in other modules) just so long as <em>at least one</em> of the datatypes is defined in the same module. This continues to permit the important use-case of extending the interfaces of existing types.</p><h3><a name="encoding-the-safety-rules-into-racket-s-macro-system"></a>Encoding the safety rules into Racket’s macro system</h3><p>In order to keep track of which methods and instances are defined where, I leveraged a technique based on the one <a href="http://www.ccs.neu.edu/racket/pubs/scheme2007-ctf.pdf">used by Typed Racket to keep track of whether or not a typed identifier is used in a typed or untyped context</a>. However, instead of using a simple mutable boolean flag, I used a mutable <a href="http://docs.racket-lang.org/syntax/syntax-helpers.html#%28tech._identifier._set%29">free identifier set</a>, which keeps track of the identifiers within a given module that should be considered “privileged”.</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/id-set</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mark-id-as-privileged!</span> +<span class="w"> </span><span class="n">id-privileged?</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="p">(</span><span class="n">mutable-free-id-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-add!</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-member?</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span></code></pre><p>Making this work with <code>define-generic</code> is obvious: just invoke <code>mark-id-as-privileged!</code> on the method name to note that the method is “privileged” in the scope of the current module. Keeping track of privileged structs is similarly straightforward, though it is a little more devious: the <code>multimethod</code> module provides a custom <code>struct</code> macro that just expands to <code>struct</code> from <code>racket/base</code>, but adds privilege information.</p><p>The <code>define-instance</code> macro does all the heavy lifting to ensure that only privileged identifiers can be used in instance definitions. A simple check for the identifier annotations is performed before proceeding with macro expansion:</p><pre><code class="pygments"><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">types</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">types</span><span class="p">)))</span></code></pre><p>When the privilege checks fail, an error is raised:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">)))</span></code></pre><p>With the above safeguards in place, the dangerous dot product implementation from above <strong>would not be allowed</strong>. The checks manage to encode both of the safety rules into the macro system such that invalid instances will fail <em>at compile time</em>, preventing dangerous uses of multimethods from ever slipping by unnoticed.</p><h3><a name="actually-implementing-multiple-dispatch"></a>Actually implementing multiple dispatch</h3><p>The rest of the multimethod implementation is relatively straightforward and is not even particularly robust. If anything, it is the bare minimum of what would be needed to allow the safety mechanisms above to work. Lots of features that would likely be needed in a real implementation are not included, and graceful error handling is largely ignored.</p><p>Multimethods themselves are implemented as Racket <a href="http://docs.racket-lang.org/guide/proc-macros.html#%28tech._transformer._binding%29">transformer bindings</a> containing custom data, including a reference to the multimethod’s arity and dispatch table. The custom datatype includes a <code>prop:procedure</code> structure type property, which allows such bindings to also function as macros. The macro procedure expands to an operation that looks up the proper instance to use in the multimethod’s dispatch table and invokes it with the supplied arguments.</p><p>The relevant code for defining multimethods is reproduced below:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="n">arity</span><span class="w"> </span><span class="n">dispatch-table</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="n">method</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">method</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="n">args</span><span class="p">))]))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-generic</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method:id</span><span class="w"> </span><span class="n">arg:id</span><span class="w"> </span><span class="n">...+</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">arity</span><span class="w"> </span><span class="p">(</span><span class="nb">length</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">arg</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="nb">make-hash</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod</span><span class="w"> </span><span class="n">arity</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">dispatch-table</span><span class="p">))))]))</span></code></pre><p>The dispatch tables are implemented entirely in terms of Racket’s structure types, so while they can be defined on arbitrary structure types (including ones defined in the Racket standard library), they <em>cannot</em> be defined on primitives such as pairs or vectors. Implementations are registered in the dispatch table using the compile-time information associated with structs’ transformer bindings, and the same information is retrieved from struct instances at runtime to look up the proper implementation to call. Notably, this only works if the struct is <code>#:transparent</code>, or more generally and accurately, if the calling code has access to the struct’s inspector. All structs defined by the <code>struct</code> form from the <code>multimethod</code> module are automatically marked as <code>#:transparent</code>.</p><p>The following code implements defining multimethod instances:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-instance</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="c1">; standard (define (proc ...) ...) shorthand</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">((</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; full (define proc lambda-expr) notation</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="n">proc:expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod-dispatch-table</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">compose1</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="n">extract-struct-info</span><span class="w"> </span><span class="nb">syntax-local-value</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))])</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">struct-types</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">hash-set!</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="n">struct-types</span><span class="w"> </span><span class="n">proc</span><span class="p">))))]))</span></code></pre><p>The resulting implementation is a useful, if certainly incomplete implementation of multimethods in Racket that does not sacrifice the safety provided by <code>racket/generic</code>’s single-dispatch approach.</p><h2><a name="related-work-advantages-and-disadvantages-and-areas-for-future-improvement"></a>Related work, advantages and disadvantages, and areas for future improvement</h2><p>As previously mentioned, this implementation of multiple dispatch was inspired by the types of APIs offered by CLOS and Clojure while also maintaining the safety of <code>racket/generic</code>. The inspiration for the safety rules came from GHC’s detection of orphan instances. Although most of the ideas presented above exist in other places, I am unsure if the concept of safety checking has been used before in any dynamically-typed programming languages.</p><p>The primary advantage offered over Racket’s existing generics system is obvious: multiple dispatch. Furthermore, this system can supersede many uses of <code>racket/generic</code> simply by dispatching on a single type. However, the current implementation does <em>not</em> support all of the features of <code>racket/generic</code>, such as supporting non-structure types and allowing fallback implementations. While those are well within the realm of possibility, other things like attaching structure type properties are probably not possible with this approach, so it is unlikely that the existing system could be subsumed by one like this one.</p><p>Additionally, this implementation would almost certainly need numerous improvements before being useful to most programmers:</p><ul><li><p><strong>Good error reporting for failure cases.</strong> Right now, even something obvious like calling a method on values that do not implement it simply fails with an error produced by <code>hash-ref</code>. In a more interesting sense, using the arity to generate compile-time error messages for <code>define-instance</code> would be a nice improvement.</p></li><li><p><strong>Support for Racket primitive data types.</strong> This might require some cooperation from Racket itself to permit an elegant implementation, but they could also just be special-cased. So long as lookup for primitives was done <em>after</em> consulting the main dispatch table, there wouldn’t be any performance hit for non-primitive types.</p></li><li><p><strong>Option to supply fallback implementations.</strong> This wouldn’t be too hard at all, though it’s questionable whether or not it would be useful without method groupings like <code>define/generic</code> provides. There would likely also need to be some sort of way to check if a set of values implements a particular method.</p></li><li><p><strong>Better cooperation with structure inspectors to alleviate the need for all structures to be transparent.</strong> It’s currently unclear to me how exactly this works and how it <em>should</em> work. There might be a better way to do this without mucking with inspectors.</p></li><li><p><strong>Much more flexible argument lists, including the ability to specify arguments that are not used for dispatch.</strong> This is really a pretty fundamental requirement, but the parsing required was significant enough for me to put it off for this initial prototype.</p></li><li><p><strong>Scribble forms to document generic methods and their instances.</strong> This is something <code>racket/generic</code> <em>doesn’t</em> have, and it has suffered for it. It would be very nice to have easy documentation forms for multimethods.</p></li><li><p><strong>Proper consideration of struct subtyping.</strong> Racket structs support subtyping, which I have not given much thought for this prototype. It is possible that subtyping violates constraints I had assumed would hold, so reviewing the existing code with that context would be useful.</p></li></ul><p>I’m not sure how much effort is involved in most of the above ideas, and in fact I’m not even completely sure how useful this system is to begin with. I have not found myself reaching much for multiple dispatch in my time as a Racket programmer, but that could simply be because it was previously unavailable. It will be interesting to see if that changes now that I have built this system, even if it is a bit rough around the edges.</p><h2><a name="conclusion"></a>Conclusion</h2><p>Despite the lack of need for multiple dispatch to solve most problems, as indicated by its general lack of support in mainstream programming languages, it’s a nice tool to have in the toolbox, and it <em>is</em> asked for in the Racket community from time to time (perhaps due to its familiarity in other parts of the Lisp world). Time will tell if pointing people to something like this will create or stifle interest in multiple dispatch for Racket.</p><p>The source for the <a href="https://github.com/lexi-lambda/racket-multimethod"><code>multimethod</code> package can be found here</a> if you are at all interested in playing with it yourself.</p><ol class="footnotes"></ol></article>ADTs in Typed Racket with macroshttps://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/https://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/21 Dec 2015<article><p>Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate <em>why</em> macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is <strong>ADTs</strong>.</p><h2><a name="warning-this-is-not-a-macro-tutorial"></a>Warning: this is not a macro tutorial</h2><p>First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you <em>are</em> interested in learning about macros, I must recommend Greg Hendershott's <a href="http://www.greghendershott.com/fear-of-macros/">Fear of Macros</a>. It is good. This is not that.</p><p>Now, with that out of the way, let's get started.</p><h2><a name="what-we-re-building"></a>What we&rsquo;re building</h2><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data types</a>, or <em>ADTs</em>, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.</p><p>Racket also already has a facility for creating custom data structures in the form of <em>structs</em>, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.</p><p>With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="kt">Tree</span></code></pre><p>This already demonstrates a few of the core things we'll need to build:</p><ol><li><p>Each ADT has a <em>data type</em>, in this case <code>Tree</code>. This name only exists in the world of types, it isn't a value.</p></li><li><p>Each ADT has various <em>data constructors</em>, in this case <code>Leaf</code> and <code>Node</code>.</p></li><li><p>Each data constructor may accept any number of arguments, each of which have a specific type.</p></li><li><p>The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.</p></li></ol><p>Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>With this in mind, we can add a fifth and final point to our list:</p><ol start="5"><li><p>ADTs must be able to be parametrically polymorphic.</p></li></ol><p>That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.</p><h3><a name="describing-adts-in-racket"></a>Describing ADTs in Racket</h3><p>How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>This looks pretty good. Just like with the Haskell implementation, <code>Tree</code> should only exist at the type level, and <code>Empty</code>, <code>Leaf</code>, and <code>Node</code> should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be <code>(Leaf 7)</code>.</p><p>Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's <code>match</code>[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">tree-sum</span><span class="w"> </span><span class="p">((</span><span class="n">Tree</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">tree</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">tree</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Node</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">l</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">r</span><span class="p">))]))</span></code></pre><p>Given that Racket's <code>struct</code> form automatically produces identifiers that cooperate with <code>match</code>, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.</p><h2><a name="implementing-adts-as-syntax"></a>Implementing ADTs as syntax</h2><p>Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. The <code>syntax/parse</code> library works like the traditional Scheme <code>syntax-case</code> on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.</p><p>Since this is not a macro tutorial, the following implementation assumes you already know how to use <code>syntax/parse</code>. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.</p><h3><a name="parsing-types-with-a-syntax-class"></a>Parsing types with a syntax class</h3><p>To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like <code>Tree</code>, or they can be identifiers with parameters, like <code>(Tree a)</code>. We'll want to cover both cases.</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">))))</span></code></pre><p>This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (<code>...+</code>) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use <code>#:attr</code> to give the <code>param</code> attribute the default value of an empty list, so this syntax class will actually <em>normalize</em> the input we get in addition to actually parsing it.</p><h3><a name="a-first-attempt-at-define-datatype"></a>A first attempt at <code>define-datatype</code></h3><p>Now we can move on to actually implementing <code>define-datatype</code>. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using <code>syntax-parser</code>, which actually does the parsing for our macro.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">]))</span></code></pre><p>This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with <code>_</code> (which will just be the identifier <code>define-datatype</code>), then expecting a <code>type-name</code>, which uses the <code>type</code> syntax class we defined above. Next, we expect zero or more <code>data-constructor</code>s, which also use the <code>type</code> syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.</p><p>Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">([</span><span class="n">f</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))]))</span></code></pre><p>This is actually really close to being correct. This will generate a struct definition for each <code>data-constructor</code>, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have <em>names</em>, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for <em>all</em> the fields, <code>f</code>, so if any data constructor has two or more fields, we'll get an error.</p><p>Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called <code>generate-temporary</code>, which generates random identifiers. Our next attempt might look like this:</p><pre><code class="pygments"><span class="o">#`</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="o">#,</span><span class="p">(</span><span class="n">generate-temporary</span><span class="p">)</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>The <code>#,</code> lets us "escape" from the template to execute <code>(generate-temporary)</code> and interpolate its result into the syntax. Unfortunately, this doesn't work. We <em>do</em> generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.</p><h3><a name="more-leveraging-syntax-classes"></a>More leveraging syntax classes</h3><p>As it turns out, this is <em>also</em> easy to do with syntax classes. We can add an extra attribute to our <code>type</code> syntax class to generate a random identifier with each one. Again, we can use <code>#:attr</code> to do that automatically. Our new definition for <code>type</code> will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span></code></pre><p>Here we're using <code>generate-temporaries</code> instead of <code>generate-temporary</code>, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each <code>param</code>.</p><p>We can now fix our macro to use this <code>field-id</code> attribute instead of the static field name:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><h3><a name="creating-the-supertype"></a>Creating the supertype</h3><p>We're almost done—now we just need to implement our overall type, the one defined by <code>type-name</code>. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">Tree</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">Leaf</span><span class="w"> </span><span class="n">Node</span><span class="p">))</span></code></pre><p>However, a polymorphic type alias would need to include the type parameters in each subtype, like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using <strong>procedural macros</strong>.</p><p>To build each properly-instantiated type, we'll use a combination of <code>define/with-syntax</code> and Racket's list comprehensions, <code>for/list</code>. The <code>define/with-syntax</code> form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by <code>syntax-parser</code>. This will allow us to break up our result into multiple steps. Technically, <code>define/with-syntax</code> is not strictly necessary—we could just use <code>#`</code> and <code>#,</code>—but it's cleaner to work with.</p><p>We'll start by defining a set of instantiated data constructor types, one per <code>data-constructor</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span></code></pre><p>Now with our definition for <code>data-type</code>, we can implement our type alias for the supertype extremely easily:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><h3><a name="putting-it-all-together"></a>Putting it all together</h3><p>There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by <code>type-name</code> are in scope for each data constructor's structure definition. We can do this by making use of <code>type-name.param</code> within each produced struct definition, resulting in this:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><p>And we're done! The final macro, now completed, looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span> + +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span><span class="w"> </span><span class="k">...</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">)))]))</span></code></pre><p>It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.</p><h2><a name="using-our-adts"></a>Using our ADTs</h2><p>With the macro built, we can now actually use our ADTs using the syntax we described! The following is now <em>valid code</em>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span> +<span class="nb">-</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">Positive-Byte</span><span class="p">)</span> +<span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span></code></pre><p>We can use this to define common data types, such as Haskell's <code>Maybe</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Nothing</span><span class="p">)</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-default</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-default</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-then</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-then</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Nothing</span><span class="p">)]))</span></code></pre><p>And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="n">Expr</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="n">Number</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Expr</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Number</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">e</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Value</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Add</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">-</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Divide</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">/</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">7</span><span class="p">))))</span> +<span class="mi">4</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, <a href="https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e">I've put together a gist here</a>.</p><h2><a name="conclusions-and-credit"></a>Conclusions and credit</h2><p>This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly <em>are</em> powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.</p><p>This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.</p><p>That said, I think it's pretty cool.</p><p>Finally, I must give credit where credit is due. Thanks to <a href="http://andmkent.com">Andrew M. Kent</a> for the creation of the <a href="https://github.com/andmkent/datatype">datatype</a> package, which served as the inspiration for this blog post. Many thanks to <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> and <a href="http://www.ccs.neu.edu/home/matthias/">Matthias Felleisen</a> for their work on creating <code>syntax/parse</code>, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.</p><p>Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.</p><ol class="footnotes"></ol></article>Functionally updating record types in Elmhttps://lexi-lambda.github.io/blog/2015/11/06/functionally-updating-record-types-in-elm/https://lexi-lambda.github.io/blog/2015/11/06/functionally-updating-record-types-in-elm/06 Nov 2015<article><p><a href="http://elm-lang.org">Elm</a> is a wonderful language for building web apps, and I love so much of its approach to language design. Elm does so many things <em>right</em> straight out of the box, and that's a real breath of fresh air in the intersection of functional programming and web development. Still, it gets one thing wrong, and unfortunately, that one thing is incredibly important. Elm took the "functions" out of "functional record types".</p><p>Almost any software program, at its core, is all about data. Maybe it's about computing data, maybe it's about manipulating data, or maybe it's about displaying data, but at the end of the day, some sort of data model is going to be needed. The functional model is a breathtakingly elegant system for handling data and shuttling it around throughout a program, and <a href="https://en.wikipedia.org/wiki/Functional_reactive_programming">functional reactive programming</a>, which Elm uses to model event-like interactions, makes this model work even better. The really important thing, though, is what tools Elm actually gives you to model your data.</p><h2><a name="a-brief-primer-on-elm-records"></a>A brief primer on Elm records</h2><p>Elm supports all the core datatypes one would expect—numbers, strings, booleans, optionals, etc.—and it allows users to define their own types with ADTs. However, Elm also provides another datatype, which it calls "records". Records are similar to objects in JavaScript: they're effectively key-value mappings. They're cool data structures, and they work well. Here's an example of creating a <code>Point</code> datatype in Elm:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">alias</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Float</span><span class="p">,</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Float</span><span class="w"> </span><span class="p">}</span></code></pre><p>Notice that <code>Point</code> is declared as a type <em>alias</em>, not as a separate type like an ADT. This is because record types are truly encoded in the type system as values with named fields, not as disparate types. This allows for some fun tricks, but that's outside the scope of this blog post.</p><h2><a name="the-good"></a>The good</h2><p>What I'd like to discuss is what it looks like to <em>manipulate</em> these data structures. Constructing them is completely painless, and reading from them is super simple. This is where the record system gets everything very <em>right</em>.</p><pre><code class="pygments"><span class="nv">origin</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">origin</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">distanceBetween</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span> +<span class="nv">distanceBetween</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nv">b</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="nv">dx</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">a</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="nv">b</span><span class="nf">.</span><span class="nv">x</span> +<span class="w"> </span><span class="nv">dy</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">a</span><span class="nf">.</span><span class="nv">y</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="nv">b</span><span class="nf">.</span><span class="nv">y</span> +<span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="nv">sqrt</span><span class="w"> </span><span class="p">(</span><span class="nv">dx</span><span class="nf">*</span><span class="nv">dx</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="nv">dy</span><span class="nf">*</span><span class="nv">dy</span><span class="p">)</span></code></pre><p>The syntax is clean and simple. Most importantly, however, the record system is functional (in the "functional programming" sense). In a functional system, it's useful to express concepts in terms of function composition, and this is very easy to do in Elm. Creating a function to access a field would normally be clunky if you always needed to do <code>record.field</code> to access the value. Fortunately, Elm provides some sugar:</p><pre><code class="pygments"><span class="c1">-- These two expressions are equivalent:</span> +<span class="p">(</span><span class="nf">\</span><span class="nv">record</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">record</span><span class="nf">.</span><span class="nv">field</span><span class="p">)</span> +<span class="nf">.</span><span class="nv">field</span></code></pre><p>Using the <code>.field</code> shorthand allows writing some other functions in terms of composition, as most functional programmers would desire:</p><pre><code class="pygments"><span class="nv">doubledX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span> +<span class="nv">doubledX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">(</span><span class="nf">(*)</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="nf">&lt;&lt;</span><span class="w"> </span><span class="nf">.</span><span class="nv">x</span></code></pre><p>This satisfies me.</p><h2><a name="the-bad"></a>The bad</h2><p>So if everything in Elm is so great, what am I complaining about? Well, while the syntax to access fields is convenient, the syntax to <em>functionally set</em> fields is questionably clunky. Consider a function that accepts a point and returns a new point with its <code>x</code> field set to <code>0</code>:</p><pre><code class="pygments"><span class="nv">zeroedX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">zeroedX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}</span></code></pre><p>This doesn't look too bad, does it? It's clear and concise. To me, though, there's something deeply wrong here... this function has a lot of redundancy! It seems to me like we should be able to write this function more clearly in a point-free style. The <code>.field</code> shorthand "functionalizes" the record getter syntax, so there must be a function version of the update syntax, right? Maybe it would look something like this:</p><pre><code class="pygments"><span class="nv">zeroedX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">zeroedX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="err">!</span><span class="nv">x</span><span class="w"> </span><span class="mi">0</span></code></pre><p>But alas, there is no such syntax.</p><p>Now you may ask... why does it matter? This seems trivial, and in fact, the explicit updater syntax may actually be more readable by virtue of how explicit it is. You'd be right, because so far, these examples have been horribly contrived. But let's consider a slightly more useful example: <em>functionally updating</em> a record.</p><p>What's the difference? Well, say I wanted to take a point and increment its <code>x</code> field by one. Well, I can easily write a function for that:</p><pre><code class="pygments"><span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span></code></pre><p>Not terrible, though a <em>little</em> verbose. Still, what if we want to also add a function that <em>decrements</em> <code>x</code>?</p><pre><code class="pygments"><span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span></code></pre><p>Oh, gosh. That's basically the exact same definition but with the operation flipped. Plus we probably want these operations for <code>y</code>, too. Fortunately, there's an easy solution: just pass a function in to <em>transform</em> the value! We can define an <code>updateX</code> function that allows us to do that easily, then we can define our derived operations in terms of that:</p><pre><code class="pygments"><span class="nv">updateX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateX</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>Not only is that much cleaner, but we can now use it to implement all sorts of other operations that allow us to add, subtract, multiply, or divide the <code>x</code> field. Now we just need to generalize our solution to work with the <code>x</code> <em>and</em> <code>y</code> fields!</p><p>Oh, wait. <strong>We can't.</strong></p><h2><a name="the-ugly"></a>The ugly</h2><p>This is where everything breaks down completely. Elm does not offer enough abstraction to reduce this level of crazy duplication:</p><pre><code class="pygments"><span class="nv">updateX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateX</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">updateY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateY</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">y</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementY</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateY</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementY</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>We sure can give it a shot, though. At the very least, we <em>can</em> implement the increment and decrement functions in a more general way by passing in an updater function:</p><pre><code class="pygments"><span class="nv">increment</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">((</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span> +<span class="nv">increment</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>Now, with <code>updateX</code> and <code>updateY</code>, we can increment either field very clearly and expressively. If we shorten the names to <code>uX</code> and <code>uY</code>, then the resulting code is actually very readable:</p><pre><code class="pygments"><span class="nv">pointAbove</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">uY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="nv">pointBelow</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">uY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>It's almost like English now: "update Y using this transformation". This is actually pretty satisfactory. The trouble arises when you have a struct with many fields:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">alias</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">strength</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">charisma</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">intellect</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="c1">-- etc.</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>It might be very convenient to have generic functional updaters in this case. One could imagine a game that has <code>Potion</code> items:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Potion</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="kt">Potion</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="p">(</span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="p">)</span></code></pre><p>And then some different kinds of potions:</p><pre><code class="pygments"><span class="nv">potions</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Health Potion"</span><span class="w"> </span><span class="p">(</span><span class="nv">uHealth</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">))),</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Greater Intellect Potion"</span><span class="w"> </span><span class="p">(</span><span class="nv">uIntellect</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Potion of Weakness"</span><span class="w"> </span><span class="p">(</span><span class="nv">uStrength</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">//</span><span class="w"> </span><span class="mi">5</span><span class="p">)))</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is a really elegant way to think about items that can affect a player's stats! Unfortunately, it also means you have to define updater functions for <em>every single field in the record</em>. This can get tedious rather quickly:</p><pre><code class="pygments"><span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uStrength</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uStrength</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">strength</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">strength</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uCharisma</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uCharisma</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">charisma</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">charisma</span><span class="w"> </span><span class="p">}</span> + +<span class="c1">-- etc.</span></code></pre><p>This is pretty icky. Could there be a better way?</p><h2><a name="trying-to-create-a-more-general-abstraction"></a>Trying to create a more general abstraction</h2><p>Interestingly, this pattern doesn't <em>need</em> to be this bad. There are better ways to do this. Let's revisit our updater functions.</p><p>Really, <code>update</code> can be defined in terms of two other primitive operations: a read and a (functional) write. What would it look like if we implemented it that way instead of requiring special updater functions to be defined? Well, it would look like this:</p><pre><code class="pygments"><span class="nv">update</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="nv">b</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="nv">b</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span> +<span class="nv">update</span><span class="w"> </span><span class="nv">get</span><span class="w"> </span><span class="nv">set</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">set</span><span class="w"> </span><span class="p">(</span><span class="nv">f</span><span class="w"> </span><span class="p">(</span><span class="nv">get</span><span class="w"> </span><span class="nv">x</span><span class="p">))</span><span class="w"> </span><span class="nv">x</span></code></pre><p>The type definition is a little long, but it's really pretty simple. We just supply a getter and a setter, then a function to do the transformation, and finally a record to actually transform. Of course, as you can see from the type, this function isn't actually specific to records: it can be used with any value for which a getter and setter can be provided.</p><p>The trouble here is that writing field setters isn't any easier in Elm than writing field updaters. They still look pretty verbose:</p><pre><code class="pygments"><span class="nv">sHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">sHealth</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="nv">sHealth</span></code></pre><p>So, at the end of it all, this isn't really a better abstraction. Still remember my fantasy <code>!field</code> setter shorthand half a blog post ago? Now perhaps it makes a little more sense. <em>If</em> such a syntax existed, then defining the updater would be incredibly simple:</p><pre><code class="pygments"><span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="err">!</span><span class="nv">health</span></code></pre><p>Still, no syntax, no easy updaters, and by extension, no easy, declarative description of behavior without quite a bit of boilerplate.</p><h2><a name="conclusions-and-related-work"></a>Conclusions and related work</h2><p>Elm is a very promising language, and it seems to be in fairly rapid development. So far, its author, <a href="https://twitter.com/czaplic">Evan Czaplicki</a>, has taken a very cautious approach to implementing language features, especially potentially redundant ones. This caution is why things like operator slicing, "where" clauses, and special updater syntax have not yet made it into the language. Maybe at some point these will be deemed important enough to include, but for the time being, they've been excluded.</p><p>I obviously think that having this sort of thing is incredibly important to being able to write expressive code without a huge amount of overhead. However, I also do <em>not</em> want to give the impression that I think adding special setter syntax is the only way to do it.</p><p>Seasoned functional programmers will surely have noticed that many of these concepts sound a lot like lenses, and Elm actually already has a lens-like library authored by Evan himself, called <a href="https://github.com/evancz/focus">Focus</a>. This, however, does not actually solve the problem: it requires manual description of setters just like the purely function based approach does. Really, lenses are just the logical next step in the line of abstraction I've already laid out above.</p><p>Interestingly, PureScript and Elm, the two Haskell-likes-on-the-frontend that I've paid attention to (though PureScript is much closer to Haskell than Elm), both have this very same problem. Haskell itself solves it with macros via Template Haskell. My favorite language, Racket, solves it with its own macro system. Is there another way to do these things that doesn't involve introducing a heavyweight macro system? Definitely. But I think this is a <em>necessary feature</em>, not a "nice to have", so if a macro system is out of the picture, then a simpler, less flexible solution is the obvious logical alternative.</p><p>I really like Elm, and most of my experiences with it have been more than enough to convince me that it is a fantastic language for the job. Unfortunately, the issue of functional record updaters has been quite the frustrating obstacle in my otherwise frictionless ride. I will continue to happily use Elm over other, far less accommodating tools, but I hope that issues like these will be smoothed out as the language and its ecosystem matures.</p><ol class="footnotes"></ol></article>Canonical factories for testing with factory_girl_apihttps://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/https://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/23 Sep 2015<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article>Managing application configuration with Envyhttps://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/https://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/30 Aug 2015<article><p>Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, <a href="http://12factor.net">The Twelve-Factor App</a> provides a set of standards for keeping web apps sane, and <a href="http://12factor.net/config">one of those guidelines advises keeping configuration in the environment</a>.</p><p><a href="https://github.com/lexi-lambda/envy">Envy</a> is the declarative bridge between Racket code and the outside world of the environment.</p><h2><a name="introducing-envy"></a>Introducing Envy</h2><p>I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require <code>envy</code> and you're good to go.</p><p>The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:</p><pre><code class="pygments"><span class="c1">; environment.rkt</span> +<span class="kn">#lang </span><span class="nn">typed/racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">envy</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/provide-environment</span> +<span class="w"> </span><span class="n">api-token</span> +<span class="w"> </span><span class="p">[</span><span class="n">log-level</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="kd">#:default</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">info</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">parallel?</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Boolean</span><span class="p">])</span></code></pre><p>When this module is required, Envy will automatically do the following:</p><ol><li><p>Envy will check the values of three environment variables: <code>API_TOKEN</code>, <code>LOG_LEVEL</code>, and <code>PARALLEL</code>.</p></li><li><p>If either <code>API_TOKEN</code> or <code>PARALLEL</code> is not set, an error will be raised:</p><pre><code>envy: The required environment variable "API_TOKEN" is not defined. +</code></pre></li><li><p>The values for <code>LOG_LEVEL</code> and <code>PARALLEL</code> will be parsed to match their type annotations.</p></li><li><p>If <code>LOG_LEVEL</code> is not set, it will use the default value, <code>'info</code>.</p></li><li><p>The values will be stored in <code>api-token</code>, <code>log-level</code>, and <code>parallel?</code>, all of which will be provided by the enclosing module.</p></li></ol><p>Now just <code>(require (prefix-in env: "environment.rkt"))</code>, and the environment variables are guaranteed to be available in your application's code.</p><h2><a name="working-with-typed-racket"></a>Working with Typed Racket</h2><p>As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, <code>define/provide-environment</code> will <em>only</em> work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.</p><p>However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.</p><pre><code>&gt; parallel? +- : Boolean +#t +</code></pre><p>Envy really shines when using optional environment variables with the <code>#:default</code> option. The type of the value given to <code>#:default</code> doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.</p><pre><code>&gt; (define-environment + [num-threads : Positive-Integer #:default #f]) +&gt; num-threads +- : (U Positive-Integer #f) +#f +</code></pre><p>This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.</p><h2><a name="and-more"></a>And more...</h2><p>To see the full set of features that Envy already provides, <a href="https://lexi-lambda.github.io/envy/envy.html">take a look at the documentation</a>. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, <a href="https://github.com/lexi-lambda/envy/issues">open an issue and make a suggestion</a>! I already have plans for a <code>#lang envy</code> DSL, which will hopefully cut the boilerplate out in its entirety.</p><p>And finally, to give credit where credit is due, Envy is heavily inspired by <a href="https://github.com/eval/envied">Envied</a> (both in name and function), an environment variable manager for Ruby, which I've used to great effect.</p><p>Try it out!</p><ul><li><p><code>raco pkg install envy</code></p></li><li><p><a href="https://github.com/lexi-lambda/envy">Envy on GitHub</a></p></li><li><p><a href="https://lexi-lambda.github.io/envy/envy.html">Envy documentation</a></p></li></ul><ol class="footnotes"></ol></article>Deploying Racket applications on Herokuhttps://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/https://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/22 Aug 2015<article><p><a href="https://www.heroku.com">Heroku</a> is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.</p><h2><a name="building-the-server"></a>Building the server</h2><p>Racket's <a href="http://docs.racket-lang.org/web-server/index.html">web-server</a> package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">web-server/servlet</span> +<span class="w"> </span><span class="n">web-server/servlet-env</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="n">req</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">response/xexpr</span> +<span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="ss">html</span><span class="w"> </span><span class="p">(</span><span class="ss">head</span><span class="w"> </span><span class="p">(</span><span class="ss">title</span><span class="w"> </span><span class="s2">"Racket Heroku App"</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="ss">body</span><span class="w"> </span><span class="p">(</span><span class="ss">h1</span><span class="w"> </span><span class="s2">"It works!"</span><span class="p">)))))</span> + +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span></code></pre><p>Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the <code>PORT</code> environment variable. We can access this using the Racket <code>getenv</code>[racket] function.</p><p>Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass <code>#f</code> for the <code>#:listen-ip</code> argument.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;number</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">))</span> +<span class="w"> </span><span class="mi">8080</span><span class="p">))</span> +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span><span class="p">)</span></code></pre><p>Also, by default, <code>serve/servlet</code>[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.</p><pre><code class="pygments"><span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span> +<span class="w"> </span><span class="kd">#:command-line?</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span></code></pre><p>That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.</p><h2><a name="setting-up-our-app-for-heroku"></a>Setting up our app for Heroku</h2><p>The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:</p><pre><code class="pygments">$<span class="w"> </span>git<span class="w"> </span>init +$<span class="w"> </span>heroku<span class="w"> </span>git:remote<span class="w"> </span>-a<span class="w"> </span>racket-heroku-sample +$<span class="w"> </span>heroku<span class="w"> </span>buildpacks:set<span class="w"> </span>https://github.com/lexi-lambda/heroku-buildpack-racket</code></pre><p>We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the <code>RACKET_VERSION</code> environment variable as follows:</p><pre><code class="pygments">$<span class="w"> </span>heroku<span class="w"> </span>config:set<span class="w"> </span><span class="nv">RACKET_VERSION</span><span class="o">=</span><span class="m">6</span>.2.1</code></pre><p>Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.</p><p>Specifically, we just want to run our <code>serve.rkt</code> module. The Racket buildpack installs the repository as a package, so we can run <code>racket</code> with the <code>-l</code> flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:</p><pre><code>web: racket -l sample-heroku-app/server +</code></pre><p>Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can <a href="https://racket-heroku-sample.herokuapp.com">navigate to our app's URL and actually see it running live</a>!</p><h2><a name="conclusion"></a>Conclusion</h2><p>That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.</p><p>That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can <a href="https://github.com/lexi-lambda/racket-sample-heroku-app">find it on GitHub here</a>. For more details on the buildpack itself, <a href="https://github.com/lexi-lambda/heroku-buildpack-racket">it's also available on GitHub here</a>.</p><ol class="footnotes"></ol></article>Automatically deploying a Frog-powered blog to GitHub pageshttps://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/https://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/18 Jul 2015<article><p>So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses <a href="http://www.greghendershott.com">Greg Hendershott</a>'s fantastic <a href="https://github.com/greghendershott/frog">Frog</a> tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via <a href="https://travis-ci.org">Travis CI</a>, so my blog is always up-to-date.</p><h2><a name="setting-up-frog"></a>Setting up Frog</h2><p>I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple <code>raco pkg install frog</code> followed by <code>raco frog --init</code> and <code>raco frog -bp</code> created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is <code>raco frog -n "Post Title"</code>, and you're good to go.</p><p>By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use <a href="http://sass-lang.com">Sass</a> for my stylesheets, potentially with support for <a href="http://coffeescript.org">CoffeeScript</a> later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used <a href="http://gulpjs.com">Gulp</a> in conjunction with <a href="https://www.npmjs.com">NPM</a> for build and dependency management.</p><p>Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.</p><h2><a name="configuring-automatic-deployment-with-travis"></a>Configuring automatic deployment with Travis</h2><p>Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being <a href="https://gist.github.com/domenic/ec8b0fc8ab45f39403dd">this Gist</a>, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special <code>gh-pages</code> branch.</p><p>To make this easy, Frog can be configured to output to a separate directory via the <code>.frogrc</code> configuration file. I chose to output to the <code>out</code> directory:</p><pre><code>output-dir = out +</code></pre><p>I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.</p><pre><code>$ cd out +$ git init +$ git add . +$ git commit -m "Deploy to GitHub Pages" +$ git push --force "$REMOTE_URL" master:gh-pages +</code></pre><p>The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's <a href="http://docs.travis-ci.com/user/encryption-keys/">encryption keys</a> along with a GitHub <a href="https://github.com/settings/tokens">personal access token</a>. Just install the Travis CLI client, copy the access token, and run a command:</p><pre><code>$ gem install travis +$ travis encrypt GH_TOKEN=&lt;access token...&gt; +</code></pre><p>The output of that command is an encrypted value to be placed in an environment variable in the project's <code>.travis.yml</code> configuration file. The URL for the repository on GitHub will also need to be specified as well:</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span></code></pre><p>Now all that's left is configuring the <code>.travis.yml</code> to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to <code>python</code>, then installed Racket and Frog as pre-installation steps.</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span></code></pre><p>(It might be worth noting that Greg Hendershott <em>also</em> maintains the repository that contains the above Travis build script!)</p><p>Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses <code>master</code>, not <code>gh-pages</code>. Obviously, I didn't want Travis running on my <code>master</code> branch, since it would be deploying to that, so I added a branch whitelist:</p><pre><code class="pygments"><span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span></code></pre><p>All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:</p><pre><code class="pygments"><span class="ch">#!/bin/bash</span> +<span class="nb">set</span><span class="w"> </span>-ev<span class="w"> </span><span class="c1"># exit with nonzero exit code if anything fails</span> + +<span class="c1"># clear the output directory</span> +rm<span class="w"> </span>-rf<span class="w"> </span>out<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">0</span><span class="p">;</span> + +<span class="c1"># build the blog files + install pygments for highlighting support</span> +npm<span class="w"> </span>install +npm<span class="w"> </span>run<span class="w"> </span>build +pip<span class="w"> </span>install<span class="w"> </span>pygments +raco<span class="w"> </span>frog<span class="w"> </span>--build + +<span class="c1"># go to the out directory and create a *new* Git repo</span> +<span class="nb">cd</span><span class="w"> </span>out +git<span class="w"> </span>init + +<span class="c1"># inside this git repo we&#39;ll pretend to be a new user</span> +git<span class="w"> </span>config<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Travis CI"</span> +git<span class="w"> </span>config<span class="w"> </span>user.email<span class="w"> </span><span class="s2">"&lt;your@email.here&gt;"</span> + +<span class="c1"># The first and only commit to this new Git repo contains all the</span> +<span class="c1"># files present with the commit message "Deploy to GitHub Pages".</span> +git<span class="w"> </span>add<span class="w"> </span>. +git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"Deploy to GitHub Pages"</span> + +<span class="c1"># Force push from the current repo&#39;s master branch to the remote</span> +<span class="c1"># repo. (All previous history on the branch will be lost, since we are</span> +<span class="c1"># overwriting it.) We redirect any output to /dev/null to hide any sensitive</span> +<span class="c1"># credential data that might otherwise be exposed.</span> +git<span class="w"> </span>push<span class="w"> </span>--force<span class="w"> </span>--quiet<span class="w"> </span><span class="s2">"https://</span><span class="si">${</span><span class="nv">GH_TOKEN</span><span class="si">}</span><span class="s2">@</span><span class="si">${</span><span class="nv">GH_REF</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>master<span class="w"> </span>&gt;<span class="w"> </span>/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span></code></pre><p>For reference, my final <code>.travis.yml</code> looked like this:</p><pre><code class="pygments"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span> +<span class="nt">python</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&#39;3.4&#39;</span> + +<span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span> + +<span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/lexi-lambda/lexi-lambda.github.io.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;long secure token...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span> + +<span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bash ./deploy.sh</span></code></pre><p>That's it! Now I have a working blog that I can publish just by pushing to the <code>source</code> branch on GitHub.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/angular.atom.xml b/feeds/angular.atom.xml new file mode 100644 index 0000000..ca75af9 --- /dev/null +++ b/feeds/angular.atom.xml @@ -0,0 +1,13 @@ +Posts tagged ‘angular’ | Alexis King’s Blog2015-09-23T00:00:00ZCanonical factories for testing with factory_girl_api2015-09-23T00:00:00Z2015-09-23T00:00:00ZAlexis King<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/angular.rss.xml b/feeds/angular.rss.xml new file mode 100644 index 0000000..9da37e3 --- /dev/null +++ b/feeds/angular.rss.xml @@ -0,0 +1,13 @@ +Posts tagged ‘angular’ | Alexis King’s BlogPosts tagged ‘angular’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/angular.html23 Sep 201523 Sep 201560Canonical factories for testing with factory_girl_apihttps://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/https://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/23 Sep 2015<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/elm.atom.xml b/feeds/elm.atom.xml new file mode 100644 index 0000000..e65c2e0 --- /dev/null +++ b/feeds/elm.atom.xml @@ -0,0 +1,66 @@ +Posts tagged ‘elm’ | Alexis King’s Blog2015-11-06T00:00:00ZFunctionally updating record types in Elm2015-11-06T00:00:00Z2015-11-06T00:00:00ZAlexis King<article><p><a href="http://elm-lang.org">Elm</a> is a wonderful language for building web apps, and I love so much of its approach to language design. Elm does so many things <em>right</em> straight out of the box, and that's a real breath of fresh air in the intersection of functional programming and web development. Still, it gets one thing wrong, and unfortunately, that one thing is incredibly important. Elm took the "functions" out of "functional record types".</p><p>Almost any software program, at its core, is all about data. Maybe it's about computing data, maybe it's about manipulating data, or maybe it's about displaying data, but at the end of the day, some sort of data model is going to be needed. The functional model is a breathtakingly elegant system for handling data and shuttling it around throughout a program, and <a href="https://en.wikipedia.org/wiki/Functional_reactive_programming">functional reactive programming</a>, which Elm uses to model event-like interactions, makes this model work even better. The really important thing, though, is what tools Elm actually gives you to model your data.</p><h2><a name="a-brief-primer-on-elm-records"></a>A brief primer on Elm records</h2><p>Elm supports all the core datatypes one would expect—numbers, strings, booleans, optionals, etc.—and it allows users to define their own types with ADTs. However, Elm also provides another datatype, which it calls "records". Records are similar to objects in JavaScript: they're effectively key-value mappings. They're cool data structures, and they work well. Here's an example of creating a <code>Point</code> datatype in Elm:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">alias</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Float</span><span class="p">,</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Float</span><span class="w"> </span><span class="p">}</span></code></pre><p>Notice that <code>Point</code> is declared as a type <em>alias</em>, not as a separate type like an ADT. This is because record types are truly encoded in the type system as values with named fields, not as disparate types. This allows for some fun tricks, but that's outside the scope of this blog post.</p><h2><a name="the-good"></a>The good</h2><p>What I'd like to discuss is what it looks like to <em>manipulate</em> these data structures. Constructing them is completely painless, and reading from them is super simple. This is where the record system gets everything very <em>right</em>.</p><pre><code class="pygments"><span class="nv">origin</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">origin</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">distanceBetween</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span> +<span class="nv">distanceBetween</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nv">b</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="nv">dx</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">a</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="nv">b</span><span class="nf">.</span><span class="nv">x</span> +<span class="w"> </span><span class="nv">dy</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">a</span><span class="nf">.</span><span class="nv">y</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="nv">b</span><span class="nf">.</span><span class="nv">y</span> +<span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="nv">sqrt</span><span class="w"> </span><span class="p">(</span><span class="nv">dx</span><span class="nf">*</span><span class="nv">dx</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="nv">dy</span><span class="nf">*</span><span class="nv">dy</span><span class="p">)</span></code></pre><p>The syntax is clean and simple. Most importantly, however, the record system is functional (in the "functional programming" sense). In a functional system, it's useful to express concepts in terms of function composition, and this is very easy to do in Elm. Creating a function to access a field would normally be clunky if you always needed to do <code>record.field</code> to access the value. Fortunately, Elm provides some sugar:</p><pre><code class="pygments"><span class="c1">-- These two expressions are equivalent:</span> +<span class="p">(</span><span class="nf">\</span><span class="nv">record</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">record</span><span class="nf">.</span><span class="nv">field</span><span class="p">)</span> +<span class="nf">.</span><span class="nv">field</span></code></pre><p>Using the <code>.field</code> shorthand allows writing some other functions in terms of composition, as most functional programmers would desire:</p><pre><code class="pygments"><span class="nv">doubledX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span> +<span class="nv">doubledX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">(</span><span class="nf">(*)</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="nf">&lt;&lt;</span><span class="w"> </span><span class="nf">.</span><span class="nv">x</span></code></pre><p>This satisfies me.</p><h2><a name="the-bad"></a>The bad</h2><p>So if everything in Elm is so great, what am I complaining about? Well, while the syntax to access fields is convenient, the syntax to <em>functionally set</em> fields is questionably clunky. Consider a function that accepts a point and returns a new point with its <code>x</code> field set to <code>0</code>:</p><pre><code class="pygments"><span class="nv">zeroedX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">zeroedX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}</span></code></pre><p>This doesn't look too bad, does it? It's clear and concise. To me, though, there's something deeply wrong here... this function has a lot of redundancy! It seems to me like we should be able to write this function more clearly in a point-free style. The <code>.field</code> shorthand "functionalizes" the record getter syntax, so there must be a function version of the update syntax, right? Maybe it would look something like this:</p><pre><code class="pygments"><span class="nv">zeroedX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">zeroedX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="err">!</span><span class="nv">x</span><span class="w"> </span><span class="mi">0</span></code></pre><p>But alas, there is no such syntax.</p><p>Now you may ask... why does it matter? This seems trivial, and in fact, the explicit updater syntax may actually be more readable by virtue of how explicit it is. You'd be right, because so far, these examples have been horribly contrived. But let's consider a slightly more useful example: <em>functionally updating</em> a record.</p><p>What's the difference? Well, say I wanted to take a point and increment its <code>x</code> field by one. Well, I can easily write a function for that:</p><pre><code class="pygments"><span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span></code></pre><p>Not terrible, though a <em>little</em> verbose. Still, what if we want to also add a function that <em>decrements</em> <code>x</code>?</p><pre><code class="pygments"><span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span></code></pre><p>Oh, gosh. That's basically the exact same definition but with the operation flipped. Plus we probably want these operations for <code>y</code>, too. Fortunately, there's an easy solution: just pass a function in to <em>transform</em> the value! We can define an <code>updateX</code> function that allows us to do that easily, then we can define our derived operations in terms of that:</p><pre><code class="pygments"><span class="nv">updateX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateX</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>Not only is that much cleaner, but we can now use it to implement all sorts of other operations that allow us to add, subtract, multiply, or divide the <code>x</code> field. Now we just need to generalize our solution to work with the <code>x</code> <em>and</em> <code>y</code> fields!</p><p>Oh, wait. <strong>We can't.</strong></p><h2><a name="the-ugly"></a>The ugly</h2><p>This is where everything breaks down completely. Elm does not offer enough abstraction to reduce this level of crazy duplication:</p><pre><code class="pygments"><span class="nv">updateX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateX</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">updateY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateY</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">y</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementY</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateY</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementY</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>We sure can give it a shot, though. At the very least, we <em>can</em> implement the increment and decrement functions in a more general way by passing in an updater function:</p><pre><code class="pygments"><span class="nv">increment</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">((</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span> +<span class="nv">increment</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>Now, with <code>updateX</code> and <code>updateY</code>, we can increment either field very clearly and expressively. If we shorten the names to <code>uX</code> and <code>uY</code>, then the resulting code is actually very readable:</p><pre><code class="pygments"><span class="nv">pointAbove</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">uY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="nv">pointBelow</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">uY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>It's almost like English now: "update Y using this transformation". This is actually pretty satisfactory. The trouble arises when you have a struct with many fields:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">alias</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">strength</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">charisma</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">intellect</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="c1">-- etc.</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>It might be very convenient to have generic functional updaters in this case. One could imagine a game that has <code>Potion</code> items:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Potion</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="kt">Potion</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="p">(</span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="p">)</span></code></pre><p>And then some different kinds of potions:</p><pre><code class="pygments"><span class="nv">potions</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Health Potion"</span><span class="w"> </span><span class="p">(</span><span class="nv">uHealth</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">))),</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Greater Intellect Potion"</span><span class="w"> </span><span class="p">(</span><span class="nv">uIntellect</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Potion of Weakness"</span><span class="w"> </span><span class="p">(</span><span class="nv">uStrength</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">//</span><span class="w"> </span><span class="mi">5</span><span class="p">)))</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is a really elegant way to think about items that can affect a player's stats! Unfortunately, it also means you have to define updater functions for <em>every single field in the record</em>. This can get tedious rather quickly:</p><pre><code class="pygments"><span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uStrength</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uStrength</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">strength</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">strength</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uCharisma</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uCharisma</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">charisma</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">charisma</span><span class="w"> </span><span class="p">}</span> + +<span class="c1">-- etc.</span></code></pre><p>This is pretty icky. Could there be a better way?</p><h2><a name="trying-to-create-a-more-general-abstraction"></a>Trying to create a more general abstraction</h2><p>Interestingly, this pattern doesn't <em>need</em> to be this bad. There are better ways to do this. Let's revisit our updater functions.</p><p>Really, <code>update</code> can be defined in terms of two other primitive operations: a read and a (functional) write. What would it look like if we implemented it that way instead of requiring special updater functions to be defined? Well, it would look like this:</p><pre><code class="pygments"><span class="nv">update</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="nv">b</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="nv">b</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span> +<span class="nv">update</span><span class="w"> </span><span class="nv">get</span><span class="w"> </span><span class="nv">set</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">set</span><span class="w"> </span><span class="p">(</span><span class="nv">f</span><span class="w"> </span><span class="p">(</span><span class="nv">get</span><span class="w"> </span><span class="nv">x</span><span class="p">))</span><span class="w"> </span><span class="nv">x</span></code></pre><p>The type definition is a little long, but it's really pretty simple. We just supply a getter and a setter, then a function to do the transformation, and finally a record to actually transform. Of course, as you can see from the type, this function isn't actually specific to records: it can be used with any value for which a getter and setter can be provided.</p><p>The trouble here is that writing field setters isn't any easier in Elm than writing field updaters. They still look pretty verbose:</p><pre><code class="pygments"><span class="nv">sHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">sHealth</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="nv">sHealth</span></code></pre><p>So, at the end of it all, this isn't really a better abstraction. Still remember my fantasy <code>!field</code> setter shorthand half a blog post ago? Now perhaps it makes a little more sense. <em>If</em> such a syntax existed, then defining the updater would be incredibly simple:</p><pre><code class="pygments"><span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="err">!</span><span class="nv">health</span></code></pre><p>Still, no syntax, no easy updaters, and by extension, no easy, declarative description of behavior without quite a bit of boilerplate.</p><h2><a name="conclusions-and-related-work"></a>Conclusions and related work</h2><p>Elm is a very promising language, and it seems to be in fairly rapid development. So far, its author, <a href="https://twitter.com/czaplic">Evan Czaplicki</a>, has taken a very cautious approach to implementing language features, especially potentially redundant ones. This caution is why things like operator slicing, "where" clauses, and special updater syntax have not yet made it into the language. Maybe at some point these will be deemed important enough to include, but for the time being, they've been excluded.</p><p>I obviously think that having this sort of thing is incredibly important to being able to write expressive code without a huge amount of overhead. However, I also do <em>not</em> want to give the impression that I think adding special setter syntax is the only way to do it.</p><p>Seasoned functional programmers will surely have noticed that many of these concepts sound a lot like lenses, and Elm actually already has a lens-like library authored by Evan himself, called <a href="https://github.com/evancz/focus">Focus</a>. This, however, does not actually solve the problem: it requires manual description of setters just like the purely function based approach does. Really, lenses are just the logical next step in the line of abstraction I've already laid out above.</p><p>Interestingly, PureScript and Elm, the two Haskell-likes-on-the-frontend that I've paid attention to (though PureScript is much closer to Haskell than Elm), both have this very same problem. Haskell itself solves it with macros via Template Haskell. My favorite language, Racket, solves it with its own macro system. Is there another way to do these things that doesn't involve introducing a heavyweight macro system? Definitely. But I think this is a <em>necessary feature</em>, not a "nice to have", so if a macro system is out of the picture, then a simpler, less flexible solution is the obvious logical alternative.</p><p>I really like Elm, and most of my experiences with it have been more than enough to convince me that it is a fantastic language for the job. Unfortunately, the issue of functional record updaters has been quite the frustrating obstacle in my otherwise frictionless ride. I will continue to happily use Elm over other, far less accommodating tools, but I hope that issues like these will be smoothed out as the language and its ecosystem matures.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/elm.rss.xml b/feeds/elm.rss.xml new file mode 100644 index 0000000..9266b4b --- /dev/null +++ b/feeds/elm.rss.xml @@ -0,0 +1,66 @@ +Posts tagged ‘elm’ | Alexis King’s BlogPosts tagged ‘elm’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/elm.html06 Nov 201506 Nov 201560Functionally updating record types in Elmhttps://lexi-lambda.github.io/blog/2015/11/06/functionally-updating-record-types-in-elm/https://lexi-lambda.github.io/blog/2015/11/06/functionally-updating-record-types-in-elm/06 Nov 2015<article><p><a href="http://elm-lang.org">Elm</a> is a wonderful language for building web apps, and I love so much of its approach to language design. Elm does so many things <em>right</em> straight out of the box, and that's a real breath of fresh air in the intersection of functional programming and web development. Still, it gets one thing wrong, and unfortunately, that one thing is incredibly important. Elm took the "functions" out of "functional record types".</p><p>Almost any software program, at its core, is all about data. Maybe it's about computing data, maybe it's about manipulating data, or maybe it's about displaying data, but at the end of the day, some sort of data model is going to be needed. The functional model is a breathtakingly elegant system for handling data and shuttling it around throughout a program, and <a href="https://en.wikipedia.org/wiki/Functional_reactive_programming">functional reactive programming</a>, which Elm uses to model event-like interactions, makes this model work even better. The really important thing, though, is what tools Elm actually gives you to model your data.</p><h2><a name="a-brief-primer-on-elm-records"></a>A brief primer on Elm records</h2><p>Elm supports all the core datatypes one would expect—numbers, strings, booleans, optionals, etc.—and it allows users to define their own types with ADTs. However, Elm also provides another datatype, which it calls "records". Records are similar to objects in JavaScript: they're effectively key-value mappings. They're cool data structures, and they work well. Here's an example of creating a <code>Point</code> datatype in Elm:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">alias</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Float</span><span class="p">,</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Float</span><span class="w"> </span><span class="p">}</span></code></pre><p>Notice that <code>Point</code> is declared as a type <em>alias</em>, not as a separate type like an ADT. This is because record types are truly encoded in the type system as values with named fields, not as disparate types. This allows for some fun tricks, but that's outside the scope of this blog post.</p><h2><a name="the-good"></a>The good</h2><p>What I'd like to discuss is what it looks like to <em>manipulate</em> these data structures. Constructing them is completely painless, and reading from them is super simple. This is where the record system gets everything very <em>right</em>.</p><pre><code class="pygments"><span class="nv">origin</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">origin</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">distanceBetween</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span> +<span class="nv">distanceBetween</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nv">b</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="nv">dx</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">a</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="nv">b</span><span class="nf">.</span><span class="nv">x</span> +<span class="w"> </span><span class="nv">dy</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">a</span><span class="nf">.</span><span class="nv">y</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="nv">b</span><span class="nf">.</span><span class="nv">y</span> +<span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="nv">sqrt</span><span class="w"> </span><span class="p">(</span><span class="nv">dx</span><span class="nf">*</span><span class="nv">dx</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="nv">dy</span><span class="nf">*</span><span class="nv">dy</span><span class="p">)</span></code></pre><p>The syntax is clean and simple. Most importantly, however, the record system is functional (in the "functional programming" sense). In a functional system, it's useful to express concepts in terms of function composition, and this is very easy to do in Elm. Creating a function to access a field would normally be clunky if you always needed to do <code>record.field</code> to access the value. Fortunately, Elm provides some sugar:</p><pre><code class="pygments"><span class="c1">-- These two expressions are equivalent:</span> +<span class="p">(</span><span class="nf">\</span><span class="nv">record</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">record</span><span class="nf">.</span><span class="nv">field</span><span class="p">)</span> +<span class="nf">.</span><span class="nv">field</span></code></pre><p>Using the <code>.field</code> shorthand allows writing some other functions in terms of composition, as most functional programmers would desire:</p><pre><code class="pygments"><span class="nv">doubledX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span> +<span class="nv">doubledX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">(</span><span class="nf">(*)</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="nf">&lt;&lt;</span><span class="w"> </span><span class="nf">.</span><span class="nv">x</span></code></pre><p>This satisfies me.</p><h2><a name="the-bad"></a>The bad</h2><p>So if everything in Elm is so great, what am I complaining about? Well, while the syntax to access fields is convenient, the syntax to <em>functionally set</em> fields is questionably clunky. Consider a function that accepts a point and returns a new point with its <code>x</code> field set to <code>0</code>:</p><pre><code class="pygments"><span class="nv">zeroedX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">zeroedX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">}</span></code></pre><p>This doesn't look too bad, does it? It's clear and concise. To me, though, there's something deeply wrong here... this function has a lot of redundancy! It seems to me like we should be able to write this function more clearly in a point-free style. The <code>.field</code> shorthand "functionalizes" the record getter syntax, so there must be a function version of the update syntax, right? Maybe it would look something like this:</p><pre><code class="pygments"><span class="nv">zeroedX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">zeroedX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="err">!</span><span class="nv">x</span><span class="w"> </span><span class="mi">0</span></code></pre><p>But alas, there is no such syntax.</p><p>Now you may ask... why does it matter? This seems trivial, and in fact, the explicit updater syntax may actually be more readable by virtue of how explicit it is. You'd be right, because so far, these examples have been horribly contrived. But let's consider a slightly more useful example: <em>functionally updating</em> a record.</p><p>What's the difference? Well, say I wanted to take a point and increment its <code>x</code> field by one. Well, I can easily write a function for that:</p><pre><code class="pygments"><span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span></code></pre><p>Not terrible, though a <em>little</em> verbose. Still, what if we want to also add a function that <em>decrements</em> <code>x</code>?</p><pre><code class="pygments"><span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span></code></pre><p>Oh, gosh. That's basically the exact same definition but with the operation flipped. Plus we probably want these operations for <code>y</code>, too. Fortunately, there's an easy solution: just pass a function in to <em>transform</em> the value! We can define an <code>updateX</code> function that allows us to do that easily, then we can define our derived operations in terms of that:</p><pre><code class="pygments"><span class="nv">updateX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateX</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>Not only is that much cleaner, but we can now use it to implement all sorts of other operations that allow us to add, subtract, multiply, or divide the <code>x</code> field. Now we just need to generalize our solution to work with the <code>x</code> <em>and</em> <code>y</code> fields!</p><p>Oh, wait. <strong>We can't.</strong></p><h2><a name="the-ugly"></a>The ugly</h2><p>This is where everything breaks down completely. Elm does not offer enough abstraction to reduce this level of crazy duplication:</p><pre><code class="pygments"><span class="nv">updateX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateX</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementX</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateX</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">updateY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">updateY</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">point</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">y</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">point</span><span class="nf">.</span><span class="nv">y</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">incrementY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">incrementY</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateY</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> + +<span class="nv">decrementY</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Point</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Point</span> +<span class="nv">decrementY</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">updateY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>We sure can give it a shot, though. At the very least, we <em>can</em> implement the increment and decrement functions in a more general way by passing in an updater function:</p><pre><code class="pygments"><span class="nv">increment</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">((</span><span class="kt">Float</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Float</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span> +<span class="nv">increment</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>Now, with <code>updateX</code> and <code>updateY</code>, we can increment either field very clearly and expressively. If we shorten the names to <code>uX</code> and <code>uY</code>, then the resulting code is actually very readable:</p><pre><code class="pygments"><span class="nv">pointAbove</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">uY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="nv">pointBelow</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">uY</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">-</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>It's almost like English now: "update Y using this transformation". This is actually pretty satisfactory. The trouble arises when you have a struct with many fields:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">alias</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">strength</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">charisma</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nv">intellect</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span> +<span class="w"> </span><span class="c1">-- etc.</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>It might be very convenient to have generic functional updaters in this case. One could imagine a game that has <code>Potion</code> items:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Potion</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="kt">Potion</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="p">(</span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="p">)</span></code></pre><p>And then some different kinds of potions:</p><pre><code class="pygments"><span class="nv">potions</span><span class="w"> </span><span class="nf">=</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Health Potion"</span><span class="w"> </span><span class="p">(</span><span class="nv">uHealth</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">1</span><span class="p">))),</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Greater Intellect Potion"</span><span class="w"> </span><span class="p">(</span><span class="nv">uIntellect</span><span class="w"> </span><span class="p">(</span><span class="nf">(+)</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">Potion</span><span class="w"> </span><span class="s">"Potion of Weakness"</span><span class="w"> </span><span class="p">(</span><span class="nv">uStrength</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="nv">x</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">//</span><span class="w"> </span><span class="mi">5</span><span class="p">)))</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is a really elegant way to think about items that can affect a player's stats! Unfortunately, it also means you have to define updater functions for <em>every single field in the record</em>. This can get tedious rather quickly:</p><pre><code class="pygments"><span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uStrength</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uStrength</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">strength</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">strength</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uCharisma</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uCharisma</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">charisma</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">stats</span><span class="nf">.</span><span class="nv">charisma</span><span class="w"> </span><span class="p">}</span> + +<span class="c1">-- etc.</span></code></pre><p>This is pretty icky. Could there be a better way?</p><h2><a name="trying-to-create-a-more-general-abstraction"></a>Trying to create a more general abstraction</h2><p>Interestingly, this pattern doesn't <em>need</em> to be this bad. There are better ways to do this. Let's revisit our updater functions.</p><p>Really, <code>update</code> can be defined in terms of two other primitive operations: a read and a (functional) write. What would it look like if we implemented it that way instead of requiring special updater functions to be defined? Well, it would look like this:</p><pre><code class="pygments"><span class="nv">update</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="nv">b</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="nv">b</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="nv">a</span> +<span class="nv">update</span><span class="w"> </span><span class="nv">get</span><span class="w"> </span><span class="nv">set</span><span class="w"> </span><span class="nv">f</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">set</span><span class="w"> </span><span class="p">(</span><span class="nv">f</span><span class="w"> </span><span class="p">(</span><span class="nv">get</span><span class="w"> </span><span class="nv">x</span><span class="p">))</span><span class="w"> </span><span class="nv">x</span></code></pre><p>The type definition is a little long, but it's really pretty simple. We just supply a getter and a setter, then a function to do the transformation, and finally a record to actually transform. Of course, as you can see from the type, this function isn't actually specific to records: it can be used with any value for which a getter and setter can be provided.</p><p>The trouble here is that writing field setters isn't any easier in Elm than writing field updaters. They still look pretty verbose:</p><pre><code class="pygments"><span class="nv">sHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">sHealth</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nv">stats</span><span class="w"> </span><span class="nf">|</span><span class="w"> </span><span class="nv">health</span><span class="w"> </span><span class="nf">&lt;-</span><span class="w"> </span><span class="nv">x</span><span class="w"> </span><span class="p">}</span> + +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="nv">sHealth</span></code></pre><p>So, at the end of it all, this isn't really a better abstraction. Still remember my fantasy <code>!field</code> setter shorthand half a blog post ago? Now perhaps it makes a little more sense. <em>If</em> such a syntax existed, then defining the updater would be incredibly simple:</p><pre><code class="pygments"><span class="nv">uHealth</span><span class="w"> </span><span class="nf">:</span><span class="w"> </span><span class="p">(</span><span class="kt">Integer</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">Integer</span><span class="p">)</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span><span class="w"> </span><span class="nf">-&gt;</span><span class="w"> </span><span class="kt">PlayerStats</span> +<span class="nv">uHealth</span><span class="w"> </span><span class="nf">=</span><span class="w"> </span><span class="nv">update</span><span class="w"> </span><span class="nf">.</span><span class="nv">health</span><span class="w"> </span><span class="err">!</span><span class="nv">health</span></code></pre><p>Still, no syntax, no easy updaters, and by extension, no easy, declarative description of behavior without quite a bit of boilerplate.</p><h2><a name="conclusions-and-related-work"></a>Conclusions and related work</h2><p>Elm is a very promising language, and it seems to be in fairly rapid development. So far, its author, <a href="https://twitter.com/czaplic">Evan Czaplicki</a>, has taken a very cautious approach to implementing language features, especially potentially redundant ones. This caution is why things like operator slicing, "where" clauses, and special updater syntax have not yet made it into the language. Maybe at some point these will be deemed important enough to include, but for the time being, they've been excluded.</p><p>I obviously think that having this sort of thing is incredibly important to being able to write expressive code without a huge amount of overhead. However, I also do <em>not</em> want to give the impression that I think adding special setter syntax is the only way to do it.</p><p>Seasoned functional programmers will surely have noticed that many of these concepts sound a lot like lenses, and Elm actually already has a lens-like library authored by Evan himself, called <a href="https://github.com/evancz/focus">Focus</a>. This, however, does not actually solve the problem: it requires manual description of setters just like the purely function based approach does. Really, lenses are just the logical next step in the line of abstraction I've already laid out above.</p><p>Interestingly, PureScript and Elm, the two Haskell-likes-on-the-frontend that I've paid attention to (though PureScript is much closer to Haskell than Elm), both have this very same problem. Haskell itself solves it with macros via Template Haskell. My favorite language, Racket, solves it with its own macro system. Is there another way to do these things that doesn't involve introducing a heavyweight macro system? Definitely. But I think this is a <em>necessary feature</em>, not a "nice to have", so if a macro system is out of the picture, then a simpler, less flexible solution is the obvious logical alternative.</p><p>I really like Elm, and most of my experiences with it have been more than enough to convince me that it is a fantastic language for the job. Unfortunately, the issue of functional record updaters has been quite the frustrating obstacle in my otherwise frictionless ride. I will continue to happily use Elm over other, far less accommodating tools, but I hope that issues like these will be smoothed out as the language and its ecosystem matures.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/envy.atom.xml b/feeds/envy.atom.xml new file mode 100644 index 0000000..77cb4df --- /dev/null +++ b/feeds/envy.atom.xml @@ -0,0 +1,18 @@ +Posts tagged ‘envy’ | Alexis King’s Blog2015-08-30T00:00:00ZManaging application configuration with Envy2015-08-30T00:00:00Z2015-08-30T00:00:00ZAlexis King<article><p>Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, <a href="http://12factor.net">The Twelve-Factor App</a> provides a set of standards for keeping web apps sane, and <a href="http://12factor.net/config">one of those guidelines advises keeping configuration in the environment</a>.</p><p><a href="https://github.com/lexi-lambda/envy">Envy</a> is the declarative bridge between Racket code and the outside world of the environment.</p><h2><a name="introducing-envy"></a>Introducing Envy</h2><p>I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require <code>envy</code> and you're good to go.</p><p>The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:</p><pre><code class="pygments"><span class="c1">; environment.rkt</span> +<span class="kn">#lang </span><span class="nn">typed/racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">envy</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/provide-environment</span> +<span class="w"> </span><span class="n">api-token</span> +<span class="w"> </span><span class="p">[</span><span class="n">log-level</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="kd">#:default</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">info</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">parallel?</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Boolean</span><span class="p">])</span></code></pre><p>When this module is required, Envy will automatically do the following:</p><ol><li><p>Envy will check the values of three environment variables: <code>API_TOKEN</code>, <code>LOG_LEVEL</code>, and <code>PARALLEL</code>.</p></li><li><p>If either <code>API_TOKEN</code> or <code>PARALLEL</code> is not set, an error will be raised:</p><pre><code>envy: The required environment variable "API_TOKEN" is not defined. +</code></pre></li><li><p>The values for <code>LOG_LEVEL</code> and <code>PARALLEL</code> will be parsed to match their type annotations.</p></li><li><p>If <code>LOG_LEVEL</code> is not set, it will use the default value, <code>'info</code>.</p></li><li><p>The values will be stored in <code>api-token</code>, <code>log-level</code>, and <code>parallel?</code>, all of which will be provided by the enclosing module.</p></li></ol><p>Now just <code>(require (prefix-in env: "environment.rkt"))</code>, and the environment variables are guaranteed to be available in your application's code.</p><h2><a name="working-with-typed-racket"></a>Working with Typed Racket</h2><p>As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, <code>define/provide-environment</code> will <em>only</em> work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.</p><p>However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.</p><pre><code>&gt; parallel? +- : Boolean +#t +</code></pre><p>Envy really shines when using optional environment variables with the <code>#:default</code> option. The type of the value given to <code>#:default</code> doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.</p><pre><code>&gt; (define-environment + [num-threads : Positive-Integer #:default #f]) +&gt; num-threads +- : (U Positive-Integer #f) +#f +</code></pre><p>This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.</p><h2><a name="and-more"></a>And more...</h2><p>To see the full set of features that Envy already provides, <a href="https://lexi-lambda.github.io/envy/envy.html">take a look at the documentation</a>. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, <a href="https://github.com/lexi-lambda/envy/issues">open an issue and make a suggestion</a>! I already have plans for a <code>#lang envy</code> DSL, which will hopefully cut the boilerplate out in its entirety.</p><p>And finally, to give credit where credit is due, Envy is heavily inspired by <a href="https://github.com/eval/envied">Envied</a> (both in name and function), an environment variable manager for Ruby, which I've used to great effect.</p><p>Try it out!</p><ul><li><p><code>raco pkg install envy</code></p></li><li><p><a href="https://github.com/lexi-lambda/envy">Envy on GitHub</a></p></li><li><p><a href="https://lexi-lambda.github.io/envy/envy.html">Envy documentation</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/envy.rss.xml b/feeds/envy.rss.xml new file mode 100644 index 0000000..ff09dac --- /dev/null +++ b/feeds/envy.rss.xml @@ -0,0 +1,18 @@ +Posts tagged ‘envy’ | Alexis King’s BlogPosts tagged ‘envy’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/envy.html30 Aug 201530 Aug 201560Managing application configuration with Envyhttps://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/https://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/30 Aug 2015<article><p>Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, <a href="http://12factor.net">The Twelve-Factor App</a> provides a set of standards for keeping web apps sane, and <a href="http://12factor.net/config">one of those guidelines advises keeping configuration in the environment</a>.</p><p><a href="https://github.com/lexi-lambda/envy">Envy</a> is the declarative bridge between Racket code and the outside world of the environment.</p><h2><a name="introducing-envy"></a>Introducing Envy</h2><p>I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require <code>envy</code> and you're good to go.</p><p>The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:</p><pre><code class="pygments"><span class="c1">; environment.rkt</span> +<span class="kn">#lang </span><span class="nn">typed/racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">envy</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/provide-environment</span> +<span class="w"> </span><span class="n">api-token</span> +<span class="w"> </span><span class="p">[</span><span class="n">log-level</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="kd">#:default</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">info</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">parallel?</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Boolean</span><span class="p">])</span></code></pre><p>When this module is required, Envy will automatically do the following:</p><ol><li><p>Envy will check the values of three environment variables: <code>API_TOKEN</code>, <code>LOG_LEVEL</code>, and <code>PARALLEL</code>.</p></li><li><p>If either <code>API_TOKEN</code> or <code>PARALLEL</code> is not set, an error will be raised:</p><pre><code>envy: The required environment variable "API_TOKEN" is not defined. +</code></pre></li><li><p>The values for <code>LOG_LEVEL</code> and <code>PARALLEL</code> will be parsed to match their type annotations.</p></li><li><p>If <code>LOG_LEVEL</code> is not set, it will use the default value, <code>'info</code>.</p></li><li><p>The values will be stored in <code>api-token</code>, <code>log-level</code>, and <code>parallel?</code>, all of which will be provided by the enclosing module.</p></li></ol><p>Now just <code>(require (prefix-in env: "environment.rkt"))</code>, and the environment variables are guaranteed to be available in your application's code.</p><h2><a name="working-with-typed-racket"></a>Working with Typed Racket</h2><p>As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, <code>define/provide-environment</code> will <em>only</em> work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.</p><p>However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.</p><pre><code>&gt; parallel? +- : Boolean +#t +</code></pre><p>Envy really shines when using optional environment variables with the <code>#:default</code> option. The type of the value given to <code>#:default</code> doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.</p><pre><code>&gt; (define-environment + [num-threads : Positive-Integer #:default #f]) +&gt; num-threads +- : (U Positive-Integer #f) +#f +</code></pre><p>This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.</p><h2><a name="and-more"></a>And more...</h2><p>To see the full set of features that Envy already provides, <a href="https://lexi-lambda.github.io/envy/envy.html">take a look at the documentation</a>. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, <a href="https://github.com/lexi-lambda/envy/issues">open an issue and make a suggestion</a>! I already have plans for a <code>#lang envy</code> DSL, which will hopefully cut the boilerplate out in its entirety.</p><p>And finally, to give credit where credit is due, Envy is heavily inspired by <a href="https://github.com/eval/envied">Envied</a> (both in name and function), an environment variable manager for Ruby, which I've used to great effect.</p><p>Try it out!</p><ul><li><p><code>raco pkg install envy</code></p></li><li><p><a href="https://github.com/lexi-lambda/envy">Envy on GitHub</a></p></li><li><p><a href="https://lexi-lambda.github.io/envy/envy.html">Envy documentation</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/frog.atom.xml b/feeds/frog.atom.xml new file mode 100644 index 0000000..37adb31 --- /dev/null +++ b/feeds/frog.atom.xml @@ -0,0 +1,79 @@ +Posts tagged ‘frog’ | Alexis King’s Blog2015-07-18T00:00:00ZAutomatically deploying a Frog-powered blog to GitHub pages2015-07-18T00:00:00Z2015-07-18T00:00:00ZAlexis King<article><p>So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses <a href="http://www.greghendershott.com">Greg Hendershott</a>'s fantastic <a href="https://github.com/greghendershott/frog">Frog</a> tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via <a href="https://travis-ci.org">Travis CI</a>, so my blog is always up-to-date.</p><h2><a name="setting-up-frog"></a>Setting up Frog</h2><p>I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple <code>raco pkg install frog</code> followed by <code>raco frog --init</code> and <code>raco frog -bp</code> created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is <code>raco frog -n "Post Title"</code>, and you're good to go.</p><p>By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use <a href="http://sass-lang.com">Sass</a> for my stylesheets, potentially with support for <a href="http://coffeescript.org">CoffeeScript</a> later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used <a href="http://gulpjs.com">Gulp</a> in conjunction with <a href="https://www.npmjs.com">NPM</a> for build and dependency management.</p><p>Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.</p><h2><a name="configuring-automatic-deployment-with-travis"></a>Configuring automatic deployment with Travis</h2><p>Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being <a href="https://gist.github.com/domenic/ec8b0fc8ab45f39403dd">this Gist</a>, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special <code>gh-pages</code> branch.</p><p>To make this easy, Frog can be configured to output to a separate directory via the <code>.frogrc</code> configuration file. I chose to output to the <code>out</code> directory:</p><pre><code>output-dir = out +</code></pre><p>I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.</p><pre><code>$ cd out +$ git init +$ git add . +$ git commit -m "Deploy to GitHub Pages" +$ git push --force "$REMOTE_URL" master:gh-pages +</code></pre><p>The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's <a href="http://docs.travis-ci.com/user/encryption-keys/">encryption keys</a> along with a GitHub <a href="https://github.com/settings/tokens">personal access token</a>. Just install the Travis CLI client, copy the access token, and run a command:</p><pre><code>$ gem install travis +$ travis encrypt GH_TOKEN=&lt;access token...&gt; +</code></pre><p>The output of that command is an encrypted value to be placed in an environment variable in the project's <code>.travis.yml</code> configuration file. The URL for the repository on GitHub will also need to be specified as well:</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span></code></pre><p>Now all that's left is configuring the <code>.travis.yml</code> to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to <code>python</code>, then installed Racket and Frog as pre-installation steps.</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span></code></pre><p>(It might be worth noting that Greg Hendershott <em>also</em> maintains the repository that contains the above Travis build script!)</p><p>Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses <code>master</code>, not <code>gh-pages</code>. Obviously, I didn't want Travis running on my <code>master</code> branch, since it would be deploying to that, so I added a branch whitelist:</p><pre><code class="pygments"><span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span></code></pre><p>All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:</p><pre><code class="pygments"><span class="ch">#!/bin/bash</span> +<span class="nb">set</span><span class="w"> </span>-ev<span class="w"> </span><span class="c1"># exit with nonzero exit code if anything fails</span> + +<span class="c1"># clear the output directory</span> +rm<span class="w"> </span>-rf<span class="w"> </span>out<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">0</span><span class="p">;</span> + +<span class="c1"># build the blog files + install pygments for highlighting support</span> +npm<span class="w"> </span>install +npm<span class="w"> </span>run<span class="w"> </span>build +pip<span class="w"> </span>install<span class="w"> </span>pygments +raco<span class="w"> </span>frog<span class="w"> </span>--build + +<span class="c1"># go to the out directory and create a *new* Git repo</span> +<span class="nb">cd</span><span class="w"> </span>out +git<span class="w"> </span>init + +<span class="c1"># inside this git repo we&#39;ll pretend to be a new user</span> +git<span class="w"> </span>config<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Travis CI"</span> +git<span class="w"> </span>config<span class="w"> </span>user.email<span class="w"> </span><span class="s2">"&lt;your@email.here&gt;"</span> + +<span class="c1"># The first and only commit to this new Git repo contains all the</span> +<span class="c1"># files present with the commit message "Deploy to GitHub Pages".</span> +git<span class="w"> </span>add<span class="w"> </span>. +git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"Deploy to GitHub Pages"</span> + +<span class="c1"># Force push from the current repo&#39;s master branch to the remote</span> +<span class="c1"># repo. (All previous history on the branch will be lost, since we are</span> +<span class="c1"># overwriting it.) We redirect any output to /dev/null to hide any sensitive</span> +<span class="c1"># credential data that might otherwise be exposed.</span> +git<span class="w"> </span>push<span class="w"> </span>--force<span class="w"> </span>--quiet<span class="w"> </span><span class="s2">"https://</span><span class="si">${</span><span class="nv">GH_TOKEN</span><span class="si">}</span><span class="s2">@</span><span class="si">${</span><span class="nv">GH_REF</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>master<span class="w"> </span>&gt;<span class="w"> </span>/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span></code></pre><p>For reference, my final <code>.travis.yml</code> looked like this:</p><pre><code class="pygments"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span> +<span class="nt">python</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&#39;3.4&#39;</span> + +<span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span> + +<span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/lexi-lambda/lexi-lambda.github.io.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;long secure token...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span> + +<span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bash ./deploy.sh</span></code></pre><p>That's it! Now I have a working blog that I can publish just by pushing to the <code>source</code> branch on GitHub.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/frog.rss.xml b/feeds/frog.rss.xml new file mode 100644 index 0000000..5b2ce5c --- /dev/null +++ b/feeds/frog.rss.xml @@ -0,0 +1,79 @@ +Posts tagged ‘frog’ | Alexis King’s BlogPosts tagged ‘frog’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/frog.html18 Jul 201518 Jul 201560Automatically deploying a Frog-powered blog to GitHub pageshttps://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/https://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/18 Jul 2015<article><p>So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses <a href="http://www.greghendershott.com">Greg Hendershott</a>'s fantastic <a href="https://github.com/greghendershott/frog">Frog</a> tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via <a href="https://travis-ci.org">Travis CI</a>, so my blog is always up-to-date.</p><h2><a name="setting-up-frog"></a>Setting up Frog</h2><p>I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple <code>raco pkg install frog</code> followed by <code>raco frog --init</code> and <code>raco frog -bp</code> created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is <code>raco frog -n "Post Title"</code>, and you're good to go.</p><p>By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use <a href="http://sass-lang.com">Sass</a> for my stylesheets, potentially with support for <a href="http://coffeescript.org">CoffeeScript</a> later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used <a href="http://gulpjs.com">Gulp</a> in conjunction with <a href="https://www.npmjs.com">NPM</a> for build and dependency management.</p><p>Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.</p><h2><a name="configuring-automatic-deployment-with-travis"></a>Configuring automatic deployment with Travis</h2><p>Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being <a href="https://gist.github.com/domenic/ec8b0fc8ab45f39403dd">this Gist</a>, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special <code>gh-pages</code> branch.</p><p>To make this easy, Frog can be configured to output to a separate directory via the <code>.frogrc</code> configuration file. I chose to output to the <code>out</code> directory:</p><pre><code>output-dir = out +</code></pre><p>I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.</p><pre><code>$ cd out +$ git init +$ git add . +$ git commit -m "Deploy to GitHub Pages" +$ git push --force "$REMOTE_URL" master:gh-pages +</code></pre><p>The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's <a href="http://docs.travis-ci.com/user/encryption-keys/">encryption keys</a> along with a GitHub <a href="https://github.com/settings/tokens">personal access token</a>. Just install the Travis CLI client, copy the access token, and run a command:</p><pre><code>$ gem install travis +$ travis encrypt GH_TOKEN=&lt;access token...&gt; +</code></pre><p>The output of that command is an encrypted value to be placed in an environment variable in the project's <code>.travis.yml</code> configuration file. The URL for the repository on GitHub will also need to be specified as well:</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span></code></pre><p>Now all that's left is configuring the <code>.travis.yml</code> to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to <code>python</code>, then installed Racket and Frog as pre-installation steps.</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span></code></pre><p>(It might be worth noting that Greg Hendershott <em>also</em> maintains the repository that contains the above Travis build script!)</p><p>Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses <code>master</code>, not <code>gh-pages</code>. Obviously, I didn't want Travis running on my <code>master</code> branch, since it would be deploying to that, so I added a branch whitelist:</p><pre><code class="pygments"><span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span></code></pre><p>All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:</p><pre><code class="pygments"><span class="ch">#!/bin/bash</span> +<span class="nb">set</span><span class="w"> </span>-ev<span class="w"> </span><span class="c1"># exit with nonzero exit code if anything fails</span> + +<span class="c1"># clear the output directory</span> +rm<span class="w"> </span>-rf<span class="w"> </span>out<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">0</span><span class="p">;</span> + +<span class="c1"># build the blog files + install pygments for highlighting support</span> +npm<span class="w"> </span>install +npm<span class="w"> </span>run<span class="w"> </span>build +pip<span class="w"> </span>install<span class="w"> </span>pygments +raco<span class="w"> </span>frog<span class="w"> </span>--build + +<span class="c1"># go to the out directory and create a *new* Git repo</span> +<span class="nb">cd</span><span class="w"> </span>out +git<span class="w"> </span>init + +<span class="c1"># inside this git repo we&#39;ll pretend to be a new user</span> +git<span class="w"> </span>config<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Travis CI"</span> +git<span class="w"> </span>config<span class="w"> </span>user.email<span class="w"> </span><span class="s2">"&lt;your@email.here&gt;"</span> + +<span class="c1"># The first and only commit to this new Git repo contains all the</span> +<span class="c1"># files present with the commit message "Deploy to GitHub Pages".</span> +git<span class="w"> </span>add<span class="w"> </span>. +git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"Deploy to GitHub Pages"</span> + +<span class="c1"># Force push from the current repo&#39;s master branch to the remote</span> +<span class="c1"># repo. (All previous history on the branch will be lost, since we are</span> +<span class="c1"># overwriting it.) We redirect any output to /dev/null to hide any sensitive</span> +<span class="c1"># credential data that might otherwise be exposed.</span> +git<span class="w"> </span>push<span class="w"> </span>--force<span class="w"> </span>--quiet<span class="w"> </span><span class="s2">"https://</span><span class="si">${</span><span class="nv">GH_TOKEN</span><span class="si">}</span><span class="s2">@</span><span class="si">${</span><span class="nv">GH_REF</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>master<span class="w"> </span>&gt;<span class="w"> </span>/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span></code></pre><p>For reference, my final <code>.travis.yml</code> looked like this:</p><pre><code class="pygments"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span> +<span class="nt">python</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&#39;3.4&#39;</span> + +<span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span> + +<span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/lexi-lambda/lexi-lambda.github.io.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;long secure token...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span> + +<span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bash ./deploy.sh</span></code></pre><p>That's it! Now I have a working blog that I can publish just by pushing to the <code>source</code> branch on GitHub.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/functional-programming.atom.xml b/feeds/functional-programming.atom.xml new file mode 100644 index 0000000..2a2d70f --- /dev/null +++ b/feeds/functional-programming.atom.xml @@ -0,0 +1,653 @@ +Posts tagged ‘functional programming’ | Alexis King’s Blog2021-03-25T00:00:00ZAn introduction to typeclass metaprogramming2021-03-25T00:00:00Z2021-03-25T00:00:00ZAlexis King<article><p><em>Typeclass metaprogramming</em> is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem), and it is the core mechanism used to implement generic programming via <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">GHC generics</a>. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.</p><p>This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does <em>not</em> attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also <em>not</em> a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.</p><h2><a name="part-1-basic-building-blocks"></a>Part 1: Basic building blocks</h2><p>Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.</p><p>To start, we’ll cover the absolute foundations of typeclass metaprogramming.</p><h3><a name="typeclasses-as-functions-from-types-to-terms"></a>Typeclasses as functions from types to terms</h3><p>As its name implies, typeclass metaprogramming (henceforth TMP<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup>) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic <code>==</code> operator via the <code>Eq</code> class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: <strong>typeclasses are functions from types to (runtime) terms</strong>.</p><p>What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called <code>TypeOf</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Given these instances, we can observe that they do what we expect in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>Note that both the <code>TypeOf Bool</code> and <code>TypeOf Char</code> instances ignore the argument to <code>typeOf</code> altogether. This makes sense, as the whole point of the <code>TypeOf</code> class is to get access to <em>type</em> information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This typeclass definition is a little unusual, as the type parameter <code>a</code> doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>the full type of the <code>show</code> method is implicitly extended with a <code>Show a</code> constraint to yield:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Furthermore, if we write <code>forall</code>s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the <em>full</em> type of <code>show</code>:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>In the same vein, we can write out the full type of <code>typeOf</code>, as given by our new definition of <code>TypeOf</code>:</p><pre><code class="pygments"><span class="nf">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This type is still unusual, as the <code>a</code> type parameter doesn’t appear anywhere to the right of the <code>=&gt;</code> arrow. This makes the type parameter trivially <em>ambiguous</em>, which is to say it’s impossible for GHC to infer what <code>a</code> should be at any call site. Fortunately, <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html">we can use <code>TypeApplications</code></a> to pass a type for <code>a</code> directly, as we can see in the updated definition of <code>TypeOf (a, b)</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Once again, we can test out our new definitions in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="kt">Bool</span> +<span class="s">"Bool"</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Bool</span><span class="p">,</span><span class="w"> </span><span class="kt">Char</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our <code>typeOf</code> function is, quite literally, a function that accepts a single type as an argument and returns a term-level <code>String</code>. Of course, the <code>TypeOf</code> typeclass is not a particularly <em>useful</em> example of such a function, but it demonstrates how easy it is to construct.</p><h3><a name="type-level-interpreters"></a>Type-level interpreters</h3><p>One important consequence of eliminating the value-level argument of <code>typeOf</code> is that there is no need for its argument type to actually be <em>inhabited</em>. For example, consider the <code>TypeOf</code> instance on <code>Void</code> from <code>Data.Void</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Void</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Void"</span></code></pre><p>This above instance is no different from the ones on <code>Bool</code> and <code>Char</code> even though <code>Void</code> is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of <code>TypeOf</code> on lists:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"["</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"]"</span></code></pre><p>If <code>typeOf</code> required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type <code>a</code> to recursively apply <code>typeOf</code> to. But since <code>typeOf</code> only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.</p><p>A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Z</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="n">a</span></code></pre><p>It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:</p><ul><li><p><code>Z</code> is a type that represents 0.</p></li><li><p><code>S Z</code> is a type that represents 1.</p></li><li><p><code>S (S Z)</code> is a type that represents 2.</p></li></ul><p>And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Numeric.Natural</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>As its name implies, <code>reifyNat</code> reifies a type-level natural number encoded using our datatypes above into a term-level <code>Natural</code> value:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="kt">Z</span> +<span class="mi">0</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span> +<span class="mi">1</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="mi">2</span></code></pre><p>One way to think about <code>reifyNat</code> is as an <em>interpreter</em> of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.</p><h3><a name="overlapping-instances"></a>Overlapping instances</h3><p>Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for <code>Show (Maybe a)</code>, you aren’t supposed to <em>also</em> write an instance for <code>Show (Maybe Bool)</code>, since it isn’t clear whether <code>show (Just True)</code> should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.</p><p>Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.</p><p>As a simple example, suppose we wanted to write a typeclass that checks whether a given type is <code>()</code> or not:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:</p><pre><code class="pygments"><span class="c1">-- not actually valid Haskell, just an example</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>But if we try to translate this to typeclass instances, we’ll get a problem:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> This means GHC will complain about instance overlap if we try to evaluate <code>isUnit @()</code>:</p><pre><code>ghci&gt; isUnit @() + +error: + • Overlapping instances for IsUnit () + arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance IsUnit () +</code></pre><p>To fix this, we have to explicitly mark <code>IsUnit ()</code> as overlapping:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>Now GHC accepts the expression without complaint:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="nb">()</span> +<span class="kt">True</span></code></pre><p>What does the <code>{-# OVERLAPPING #-}</code> pragma do, exactly? The gory details are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances">spelled out in the GHC User’s Guide</a>, but the simple explanation is that <code>{-# OVERLAPPING #-}</code> relaxes the overlap checker as long as the instance is <em>strictly more specific</em> than the instance(s) it overlaps with. In this case, that is true: <code>IsUnit ()</code> is trivially more specific than <code>IsUnit a</code>, since the former only matches <code>()</code> while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.</p><p>Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>The intent of <code>guardUnit</code> is to use <code>isUnit</code> to detect if its argument is of type <code>()</code>, and if it is, to return an error. However, even though we marked <code>IsUnit ()</code> overlapping, we still get an overlapping instance error:</p><pre><code>error: + • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance [overlapping] IsUnit () + • In the expression: isUnit @a +</code></pre><p>What gives? The problem is that GHC simply doesn’t know what type <code>a</code> is when compiling <code>guardUnit</code>. It <em>could</em> be instantiated to <code>()</code> where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.</p><p>This behavior is actually a very, very good thing. If GHC were to blindly pick the <code>IsUnit a</code> instance in this case, then <code>guardUnit</code> would always take the <code>False</code> branch, even when passed a value of type <code>()</code>! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when <code>{-# OVERLAPPING #-}</code> annotations are used, so it’s important to keep their limitations in mind.</p><p>As it happens, in this particular case, the error is easily remedied. We simply have to add an <code>IsUnit</code> constraint to the type signature of <code>guardUnit</code>:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now picking the right <code>IsUnit</code> instance is deferred to the place where <code>guardUnit</code> is used, and the definition is accepted.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><h3><a name="type-families-are-functions-from-types-to-types"></a>Type families are functions from types to types</h3><p>In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE TypeFamilies #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>The above is a <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families">closed type family</a>, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of <code>Sum</code> would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="nf">sum</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span> +<span class="nf">sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="nf">sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="n">sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.</p><p>To test our definition of <code>Sum</code> in GHCi, we can use <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind">the <code>:kind!</code> command</a>, which prints out a type and its kind after reducing it as much as possible:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span></code></pre><p>We can also combine <code>Sum</code> with our <code>ReifyNat</code> class from earlier:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.</p><h3><a name="example-1-generalized-concat"></a>Example 1: Generalized <code>concat</code></h3><p>Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a <code>flatten</code> function similar to like-named functions provided by many dynamically-typed languages. In those languages, <code>flatten</code> is like <code>concat</code>, but it works on a list of arbitrary depth. For example, we might use it like this:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]</span></code></pre><p>In Haskell, lists of different depths have different types, so multiple levels of <code>concat</code> have to be applied explicitly. But using TMP, we can write a generic <code>flatten</code> function that operates on lists of any depth!</p><p>Since this is <em>typeclass</em> metaprogramming, we’ll unsurprisingly begin with a typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="o">???</span><span class="p">]</span></code></pre><p>Our first challenge is writing the return type of <code>flatten</code>. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>Now we can write our <code>Flatten</code> instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>The inductive case is when the type is a nested list, in which case we want to apply <code>concat</code> and recur:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">(</span><span class="n">concat</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Sadly, if we try to compile these definitions, GHC will reject our <code>Flatten [a]</code> instance:</p><pre><code>error: + • Couldn't match type ‘a’ with ‘ElementOf [a]’ + ‘a’ is a rigid type variable bound by + the instance declaration + Expected type: [ElementOf [a]] + Actual type: [a] + • In the expression: x + In an equation for ‘flatten’: flatten x = x + In the instance declaration for ‘Flatten [a]’ + | + | flatten x = x + | ^ +</code></pre><p>At first blush, this error looks very confusing. Why doesn’t GHC think <code>a</code> and <code>ElementOf [a]</code> are the same type? Well, consider what would happen if we picked a type like <code>[Int]</code> for <code>a</code>. Then <code>[a]</code> would be <code>[[Int]]</code>, a nested list, so the first case of <code>ElementOf</code> would apply. Therefore, GHC refuses to pick the second equation of <code>ElementOf</code> so hastily.</p><p>In this particular case, we might think that’s rather silly. After all, if <code>a</code> were <code>[Int]</code>, then GHC wouldn’t have picked the <code>Flatten [a]</code> instance to begin with, it would pick the more specific <code>Flatten [[a]]</code> instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.</p><p>Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our <code>Flatten [a]</code> instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>This is a <em>type equality constraint</em>. Type equality constraints are written with the syntax <code>a ~ b</code>, and they state that <code>a</code> must be the same type as <code>b</code>. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that <code>ElementOf [a]</code> must always be <code>a</code>, which allows the instance to typecheck.</p><p>Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must <em>eventually</em> be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the <code>Flatten [a]</code> instance is selected, GHC will know that <code>a</code> is <em>not</em> a list type, and it will be able to reduce <code>ElementOf [a]</code> to <code>a</code> without difficulty. Indeed, we can see this for ourselves by using <code>flatten</code> in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span></code></pre><p>It works! But why do we need the type annotation on <code>1</code>? If we leave it out, we get a rather hairy type error:</p><pre><code>error: + • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ + Expected type: [ElementOf [a]] + Actual type: [ElementOf [a0]] + NB: ‘ElementOf’ is a non-injective type family + The type variable ‘a0’ is ambiguous +</code></pre><p>The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a <code>Num [a]</code> instance, in which case <code>1</code> could actually have a list type, and either case of <code>ElementOf</code> could match depending on the choice of <code>Num</code> instance. Of course, no such <code>Num</code> instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.</p><p>This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call <code>flatten</code> on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.</p><p>That wrinkle aside, <code>flatten</code> is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.</p><h4><a name="typeclasses-as-compile-time-code-generation"></a>Typeclasses as compile-time code generation</h4><p>Presented with the above definition of <code>Flatten</code>, it might not be immediately obvious how to think about <code>Flatten</code> as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, <code>Eq</code> or <code>Show</code>) than the <code>TypeOf</code> and <code>ReifyNat</code> classes we defined above.</p><p>One useful way to shift our perspective is to consider equivalent <code>Flatten</code> instances written using point-free style:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">id</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">concat</span></code></pre><p>These definitions of <code>flatten</code> no longer (syntactically) depend on term-level arguments, just like our definitions of <code>typeOf</code> and <code>reifyNat</code> didn’t accept any term-level arguments above. This allows us to consider what <code>flatten</code> might “expand to” given a type argument alone:</p><ul><li><p><code>flatten @[Int]</code> is just <code>id</code>, since the <code>Flatten [a]</code> instance is selected.</p></li><li><p><code>flatten @[[Int]]</code> is <code>flatten @[Int] . concat</code>, since the <code>Flatten [[a]]</code> instance is selected. That then becomes <code>id . concat</code>, which can be further simplified to just <code>concat</code>.</p></li><li><p><code>flatten @[[[Int]]]</code> is <code>flatten @[[Int]] . concat</code>, which simplifies to <code>concat . concat</code> by the same reasoning above.</p></li><li><p><code>flatten @[[[[Int]]]]</code> is then <code>concat . concat . concat</code>, and so on.</p></li></ul><p>This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of <code>flatten</code> takes a type as an argument and produces some number of composed <code>concat</code>s as a result. From this perspective, <code>Flatten</code> is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.</p><p>This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name <em>metaprogramming</em>. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.</p><h2><a name="part-2-generic-programming"></a>Part 2: Generic programming</h2><p>Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.</p><p>In the previous section, we discussed how to use TMP to write a generic <code>flatten</code> operation. In this section, we’ll aim a bit higher: totally generic functions that operate on <em>arbitrary</em> datatypes.</p><h3><a name="open-type-families-and-associated-types"></a>Open type families and associated types</h3><p>Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, <em>open type families</em>. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using <code>type instance</code> declarations. For example, we could define our <code>Sum</code> family from above like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>In the case of <code>Sum</code>, this would not be very useful, and indeed, <code>Sum</code> is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.</p><p>This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a <code>Key</code> open type family that relates types to the types used to index them:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span></code></pre><p>This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>In this case, anyone could define their own data structure, define instances of <code>Key</code> and <code>HasKey</code> for their data structure, and use <code>hasKey</code> to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>An open family declared inside a typeclass like this is called an <em>associated type</em>. It works exactly the same way as the separate definitions of <code>Key</code> and <code>HasKey</code>, it just uses a different syntax. Note that although the <code>family</code> and <code>instance</code> keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).</p><p>Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like <a href="https://hackage.haskell.org/package/mono-traversable"><code>mono-traversable</code></a> are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.</p><p>However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes <em>heavy</em> use of open type families: datatype-generic programming.</p><h3><a name="example-2-datatype-generic-programming"></a>Example 2: Datatype-generic programming</h3><p><em>Datatype-generic programming</em> refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include</p><ul><li><p>equality, comparison, and hashing,</p></li><li><p>recursive traversal of self-similar data structures, and</p></li><li><p>serialization and deserialization,</p></li></ul><p>among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on <em>any</em> datatype.</p><p>In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.</p><h4><a name="generic-datatype-representations"></a>Generic datatype representations</h4><p>At the heart of the <code>Generic</code> class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Authentication</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AuthBasic</span><span class="w"> </span><span class="kt">Username</span><span class="w"> </span><span class="kt">Password</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AuthSSH</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>then we have a type that is essentially equivalent to this one:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>If we know how to define a function on a nested tree built out of <code>Either</code>s and pairs, then we know how to define it on <em>any</em> such datatype! This is where TMP comes in: recall the way we viewed <code>Flatten</code> as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?</p><p>The answer to that question is <em>yes</em>. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, <code>numFields (AuthBasic "alyssa" "pass1234")</code> would return <code>2</code>, while <code>numFields (AuthSSH "&lt;key&gt;")</code> would return <code>1</code>. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.</p><p>We’ll start by using TMP to implement a “generic” version of <code>numFields</code> that operates on trees of <code>Either</code>s and pairs as described above:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="c1">-- base case: leaf value</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>Just like our <code>Flatten</code> class from earlier, <code>GNumFields</code> uses the type-level structure of its argument to choose what to do:</p><ul><li><p>If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.</p></li><li><p>If we find <code>Left</code> or <code>Right</code>, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.</p></li><li><p>In the case of any other value, we’re at a “leaf” in the tree of <code>Either</code>s and pairs, which corresponds to a single field, so we just return <code>1</code>.</p></li></ul><p>Now if we call <code>gnumFields (Left ("alyssa", "pass1234"))</code>, we’ll get <code>2</code>, and if we call <code>gnumFields (Right "&lt;key&gt;")</code>, we’ll get <code>1</code>. All that’s left to do is write a bit of code that converts our <code>Authentication</code> type to a tree of <code>Either</code>s and pairs:</p><pre><code class="pygments"><span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericizeAuthentication</span></code></pre><p>Now we get the results we want on our <code>Authentication</code> type using <code>numFieldsAuthentication</code>, but we’re not done yet, since it only works on <code>Authentication</code> values. Is there a way to define a generic <code>numFields</code> function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFields</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericize</span></code></pre><p>Now <code>numFields (AuthBasic "alyssa" "pass1234")</code> returns <code>2</code>, as desired, and it will <em>also</em> work with any datatype that provides a <code>Generic</code> instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:</p><ul><li><p>First, we define the <code>Generic</code> class, comprised of two parts:</p><ol><li><p>The <code>Rep a</code> associated type maps a type <code>a</code> onto its generic, sums-of-products representation, i.e. one built out of combinations of <code>Either</code> and pairs.</p></li><li><p>The <code>genericize</code> method converts an actual <em>value</em> of type <code>a</code> to the equivalent value using the sums-of-products representation.</p></li></ol></li><li><p>Next, we define a <code>Generic</code> instance for <code>Authentication</code>. <code>Rep Authentication</code> is the sums-of-products representation we described above, and <code>genericize</code> is likewise <code>genericizeAuthentication</code> from above.</p></li><li><p>Finally, we define <code>numFields</code> as a function with a <code>GNumFields (Rep a)</code> constraint. This is where all the magic happens:</p><ul><li><p>When we apply <code>numFields</code> to a datatype, <code>Rep</code> retrieves its generic, sums-of-products representation type.</p></li><li><p>The <code>GNumFields</code> class then uses various TMP techniques we’ve already described so far in this blog post to generate a <code>numFields</code> implementation on the fly from the structure of <code>Rep a</code>.</p></li><li><p>Finally, that generated <code>numFields</code> implementation is applied to the genericized term-level value, and the result is produced.</p></li></ul></li></ul><p>After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) <code>numFields</code> operation. Surely just defining <code>numFields</code> on each type directly would be far easier? Indeed, if we were just considering <code>numFields</code>, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined <code>numFields</code>, and all of them would automatically work on <code>Authentication</code> because they all leverage the same <code>Generic</code> instance!</p><p>This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation <em>once</em>, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.</p><h4><a name="improving-our-definition-of-generic"></a>Improving our definition of <code>Generic</code></h4><p>You may note that the definition of <code>Generic</code> provided above does not match the one in <code>GHC.Generic</code>. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a <code>GHC.Generics</code> tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.</p><h5><a name="distinguishing-leaves-from-the-spine"></a>Distinguishing leaves from the spine</h5><p>One problem with our version of <code>Generic</code> is that it provides no way to distinguish an <code>Either</code> or pair that should be considered a “leaf”, as in a type like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">A</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">B</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">)</span></code></pre><p>Given this type, <code>Rep Foo</code> should be <code>Either (Either Int String) (Char, Bool)</code>, and <code>numFields (Right ('a', True))</code> will erroneously return <code>2</code> rather than <code>1</code>. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">getLeaf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Now our <code>Generic</code> instances look like this:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">PublicKey</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">key</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">))</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">A</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">B</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Since the <code>Leaf</code> constructor now distinguishes a leaf, rather than the absence of an <code>Either</code> or <code>(,)</code> constructor, we’ll have to update our <code>GNumFields</code> instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.</p><h5><a name="handling-empty-constructors"></a>Handling empty constructors</h5><p>Suppose we have a type with nullary data constructors, like the standard <code>Bool</code> type:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>How do we write a <code>Generic</code> instance for <code>Bool</code>? Using just <code>Either</code>, <code>(,)</code>, and <code>Leaf</code>, we can’t, but if we are willing to add a case for <code>()</code>, we can use it to denote nullary constructors:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="nb">()</span></code></pre><p>In a similar vein, we could use <code>Void</code> to represent datatypes that don’t have any constructors at all.</p><h4><a name="continuing-from-here"></a>Continuing from here</h4><p>The full version of <code>Generic</code> has a variety of further improvements useful for generic programming, including:</p><ul><li><p>Support for converting from <code>Rep a</code> to <code>a</code>.</p></li><li><p>Special indication of self-recursive datatypes, making generic tree traversals possible.</p></li><li><p>Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.</p></li><li><p>Fully automatic generation of <code>Generic</code> instances via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric">the <code>DeriveGeneric</code> extension</a>, which reduces the per-type boilerplate to essentially nothing.</p></li></ul><p>The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.</p><h2><a name="part-3-dependent-typing"></a>Part 3: Dependent typing</h2><p>It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.</p><h3><a name="datatype-promotion"></a>Datatype promotion</h3><p>In part 1, we used uninhabited datatypes like <code>Z</code> and <code>S a</code> to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, <code>True</code> and <code>False</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">True</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">False</span></code></pre><p>Now we could define type families to provide operations on these types, such as <code>Not</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>However, this has some frustrating downsides:</p><ul><li><p>First, it’s simply inconvenient that we have to define these new <code>True</code> and <code>False</code> “dummy” types, which are completely distinct from the <code>Bool</code> type provided by the prelude.</p></li><li><p>More significantly, it means <code>Not</code> has a very unhelpful kind:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span></code></pre><p>Even though <code>Not</code> is only <em>supposed</em> to be applied to <code>True</code> or <code>False</code>, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like <code>Not Char</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span> +<span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span></code></pre><p>Rather than getting an error, GHC simply spits <code>Not Char</code> back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.</p></li></ul><p>One way to think about <code>Not</code> is that it is largely <em>dynamically kinded</em> in the same way some languages are dynamically typed. That isn’t entirely true, as we technically <em>will</em> get a kind error if we try to apply <code>Not</code> to a type constructor rather than a type, such as <code>Maybe</code>:</p><pre><code>ghci&gt; :kind! Not Maybe + +&lt;interactive&gt;:1:5: error: + • Expecting one more argument to ‘Maybe’ + Expected a type, but ‘Maybe’ has kind ‘* -&gt; *’ +</code></pre><p>…but <code>*</code> is still a very big kind, much bigger than we would like to permit for <code>Not</code>.</p><p>To help with both these problems, GHC provides <em>datatype promotion</em> via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html">the <code>DataKinds</code> language extension</a>. The idea is that for each normal, non-GADT type definition like</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>then in addition to the normal type constructor and value constructors, GHC also defines several <em>promoted</em> constructors:</p><ul><li><p><code>Bool</code> is allowed as both a type and a kind.</p></li><li><p><code>'True</code> and <code>'False</code> are defined as new types of kind <code>Bool</code>.</p></li></ul><p>We can see this in action if we remove our <code>data True</code> and <code>data False</code> declarations and adjust our definition of <code>Not</code> to use promoted constructors:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DataKinds #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;True</span></code></pre><p>Now the inferred kind of <code>Not</code> is no longer <code>* -&gt; *</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>Consequently, we will now get a kind error if we attempt to apply <code>Not</code> to anything other than <code>'True</code> or <code>'False</code>:</p><pre><code>ghci&gt; :kind! Not Char + +&lt;interactive&gt;:1:5: error: + • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ +</code></pre><p>This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">&#39;Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">&#39;S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>Note that we need to add an explicit kind signature on the definition of the <code>ReifyNat</code> typeclass, since otherwise GHC will assume <code>a</code> has kind <code>*</code>, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that <code>Z</code> and <code>S</code> are related, this prevents someone from coming along and defining a nonsensical instance like <code>ReifyNat Char</code>, which previously would have been allowed but will now be rejected with a kind error.</p><p>Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.</p><h3><a name="gadts-and-proof-terms"></a>GADTs and proof terms</h3><p>So far in this blog post, we have discussed several different function-like things:</p><ul><li><p>Ordinary Haskell functions are functions from terms to terms.</p></li><li><p>Type families are functions from types to types.</p></li><li><p>Typeclasses are functions from types to terms.</p></li></ul><p>A curious reader may wonder about the existence of a fourth class of function:</p><ul><li><p><em>???</em> are functions from terms to types.</p></li></ul><p>To reason about what could go in the <em>???</em> above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?</p><p>In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.</p><p>GADTs<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup> are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html">described in detail in the GHC User’s Guide</a>, but the key idea for our purposes is that <em>pattern-matching on a GADT constructor can refine type information</em>. Here’s a simple, silly example:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Bool</span> +<span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Int</span> + +<span class="nf">doSomething</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">x</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span></code></pre><p>Here, <code>WhatIsIt</code> is a datatype with two nullary constructors, <code>ABool</code> and <code>AnInt</code>, similar to a normal, non-GADT datatype like this one:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AnInt</span></code></pre><p>What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, <code>ABool</code> and <code>AnInt</code> would both have the type <code>forall a. WhatIsIt a</code>, but in the GADT definition, we explicitly fix <code>a</code> to <code>Bool</code> in the type of <code>ABool</code> and to <code>Int</code> in the type of <code>AnInt</code>.</p><p>This simple feature allows us to do very interesting things. The <code>doSomething</code> function is polymorphic in <code>a</code>, but on the right-hand side of the first equation, <code>x</code> has type <code>Bool</code>, while on the right-hand side of the second equation, <code>x</code> has type <code>Int</code>. This is because the <code>WhatIsIt a</code> argument effectively constrains the type of <code>a</code>, as we can see by experimenting with <code>doSomething</code> in GHCi:</p><pre><code>ghci&gt; doSomething ABool True +False +ghci&gt; doSomething AnInt 10 +11 +ghci&gt; doSomething AnInt True + +error: + • Couldn't match expected type ‘Int’ with actual type ‘Bool’ + • In the second argument of ‘doSomething’, namely ‘True’ + In the expression: doSomething AnInt True + In an equation for ‘it’: it = doSomething AnInt True +</code></pre><p>One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The <code>ABool</code> constructor is a proof of <code>a ~ Bool</code>, while the <code>AnInt</code> constructor is a proof of <code>a ~ Int</code>. When you construct <code>ABool</code> or <code>AnInt</code>, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.</p><p>GADTs can be much more sophisticated than our simple <code>WhatIsIt</code> type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:</p><pre><code class="pygments"><span class="kr">infixr</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span> + +<span class="kr">data</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">HCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This type is a <em>heterogenous list</em>, a list that can contain elements of different types:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">t</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Num</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[Bool, [Char]</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>An <code>HList</code> is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total <code>head</code> function on <code>HList</code>s like this:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Remarkably, GHC does not complain that this definition of <code>head</code> is non-exhaustive. Since we specified that the argument must be of type <code>HList (a ': as)</code> in the type signature for <code>head</code>, GHC knows that the argument <em>cannot</em> be <code>HNil</code> (which would have the type <code>HList '[]</code>), so it doesn’t ask us to handle that case.</p><p>These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.</p><h4><a name="proofs-that-work-together"></a>Proofs that work together</h4><p>This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an <code>HList</code> of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with <code>head</code>, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.</p><p>However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a <em>proof term</em> that contains no values, it just encapsulates type equalities on a type-level list:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a]</span> +<span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b]</span> +<span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b, c]</span></code></pre><p>We call it a proof term because a value of type <code>OneToThree a b c as</code> constitutes a <em>proof</em> that <code>as</code> has exactly 1, 2, or 3 elements. Using <code>OneToThree</code>, we can write a function that accepts an <code>HList</code> accompanied by a proof term:</p><pre><code class="pygments"><span class="nf">sumUpToThree</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">z</span></code></pre><p>As with <code>head</code>, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:</p><ul><li><p>When we match on the <code>OneToThree</code> proof term, information flows from the term level to the type level, refining the type of <code>as</code> in that branch.</p></li><li><p>The refined type of <code>as</code> then flows back down to the term level, restricting the shape the <code>HList</code> can take and refinine the set of patterns we have to match.</p></li></ul><p>Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an <code>HList</code> has an even number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This is a proof which itself has inductive structure: <code>EvenCons</code> takes a proof that <code>as</code> has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an <code>HList</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span> + +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Once again, this definition is completely exhaustive, and we can show that it works in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="kt">EvenNil</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.</p><h4><a name="proof-inference"></a>Proof inference</h4><p>While our definition of <code>pairUp</code> is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the <code>Even</code> proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.</p><p>Remember that typeclasses are functions from types to terms. As its happens, a value of type <code>Even as</code> can be mechanically produced from the structure of the type <code>as</code>. This suggests that we could use TMP to automatically generate <code>Even</code> proofs, and indeed, we can. In fact, it’s not at all complicated:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>We can now adjust our <code>pairUp</code> function to use <code>IsEven</code> instead of an explicit <code>Even</code> argument:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>This is essentially identical to its old definition, but by acquiring the proof via <code>IsEven</code> rather than passing it explicitly, we can call <code>pairUp</code> without having to construct a proof manually:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This is rather remarkable. Using TMP, we are able to get GHC to <em>automatically construct a proof that a list is even</em>, with no programmer guidance beyond writing the <code>IsEven</code> typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: <code>IsEven</code> is a function that accepts a type-level list and generates an <code>Even</code> proof term.</p><p>From this perspective, <strong>typeclasses are a way of specifying a proof search algorithm</strong> to the compiler. In the case of <code>IsEven</code>, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.</p><h3><a name="aside-gadts-versus-type-families"></a>Aside: GADTs versus type families</h3><p>Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.</p><p>Consider again the type of the <code>pairUp</code> function above (without the typeclass for simplicity):</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>We used both a GADT, <code>Even</code>, and a type family, <code>PairUp</code>. But we could have, in theory, used <em>only</em> a GADT and eliminated the type family altogether. Consider this variation on the <code>Even</code> proof term:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="p">((</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span></code></pre><p>This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of <code>as</code> as an “input” parameter and <code>bs</code> as an “output” parameter. The idea is that any <code>EvenPairs</code> proof relates both an even-length list type and its paired up equivalent:</p><ul><li><p><code>EvenNil</code> has type <code>EvenPairs '[] '[]</code>,</p></li><li><p><code>EvenCons EvenNil</code> has type <code>EvenPairs '[a, b] '[(a, b)]</code>,</p></li><li><p><code>EvenCons (EvenCons EvenNil)</code> has type <code>EvenPairs '[a, b, c, d] '[(a, b), (c, d)]</code>,</p></li><li><p>…and so on.</p></li></ul><p>This allows us to reformulate our <code>pairUp</code> type signature this way:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">bs</span></code></pre><p>The definition is otherwise unchanged. The <code>PairUp</code> type family is completely gone, because now <code>EvenPairs</code> itself defines the relation. In this way, GADTs can be used like type-level functions!</p><p>The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Kind</span><span class="w"> </span><span class="p">(</span><span class="kt">Constraint</span><span class="p">)</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span></code></pre><p>The idea here is that <code>IsEvenTF as</code> produces a constraint can only be satisfied if <code>as</code> has an even number of elements, since that’s the only way it will eventually reduce to <code>()</code>, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting <code>IsEvenTF as =&gt;</code> in a type signature successfully restricts <code>as</code> to be an even-length list, but it doesn’t allow us to write <code>pairUp</code>. To see why, we can try the following definition:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Unlike the version using the GADT, this version of <code>pairUp</code> is not considered exhaustive:</p><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil +</code></pre><p>This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by <code>IsEvenTF</code> provides no term-level evidence about the shape of <code>as</code>, so we can’t branch on it the way we can branch on the <code>Even</code> GADT.<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> (In a sense, <code>IsEvenTF</code> is doing <a href="/blog/2019/11/05/parse-don-t-validate/">validation, not parsing</a>.)</p><p>For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.</p><h3><a name="guiding-type-inference"></a>Guiding type inference</h3><p>So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.</p><p>However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.</p><p>To illustrate what that can look like, suppose we want to use TMP to generate an <code>HList</code> full of <code>()</code> values of an arbitrary length:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Testing in GHCi, we can see it behaves as desired:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[(), (), ()]</span> +<span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>Now suppose we write a function that accepts a list containing exactly one element and returns it:</p><pre><code class="pygments"><span class="nf">unsingleton</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[a]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unsingleton</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Naturally, we would expect these to compose without a hitch. If we write <code>unsingleton unitList</code>, our TMP should generate a list of length 1, and we should get back <code>()</code>. However, it may surprise you to learn that <em>isn’t</em>, in fact, what happens:<sup><a href="#footnote-6" id="footnote-ref-6-1">6</a></sup></p><pre><code>ghci&gt; unsingleton unitList + +error: + • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ + prevents the constraint ‘(UnitList '[a0])’ from being solved. + Probable fix: use a type annotation to specify what ‘a0’ should be. + These potential instances exist: + instance UnitList as =&gt; UnitList (() : as) +</code></pre><p>What went wrong? The type error says that <code>a0</code> is ambiguous, but it only lists a single matching <code>UnitList</code> instance—the one we want—so how can it be ambiguous which one to select?</p><p>The problem stems from the way we defined <code>UnitList</code>. When we wrote the instance</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span></code></pre><p>we said the first element of the type-level list must be <code>()</code>, so there’s nothing stopping someone from coming along and defining another instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="kt">Int</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>In that case, GHC would have no way to know which instance to pick. Nothing in the type of <code>unsingleton</code> forces the element in the list to have type <code>()</code>, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.</p><p>Of course, this isn’t what we want. The <code>UnitList</code> class is supposed to <em>always</em> return a list of <code>()</code> values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Here we’ve changed the instance so that it has the shape <code>UnitList (a ': as)</code>, with a type variable in place of the <code>()</code>, but we also added an equality constraint that forces <code>a</code> to be <code>()</code>. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unsingleton</span><span class="w"> </span><span class="n">unitList</span> +<span class="nb">()</span></code></pre><p>To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="o">&lt;</span><span class="n">constraints</span><span class="o">&gt;</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">C</span><span class="w"> </span><span class="o">&lt;</span><span class="n">types</span><span class="o">&gt;</span></code></pre><p>The part to the left of the <code>=&gt;</code> is known as the <em>instance context</em>, while the part to the right is known as the <em>instance head</em>. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, <strong>only the instance head matters, and the instance context is completely ignored</strong>. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.</p><p>This explains why our two <code>UnitList</code> instances behave differently:</p><ul><li><p>Given the instance head <code>UnitList (() ': as)</code>, GHC won’t select the instance unless it knows the first element of the list is <code>()</code>.</p></li><li><p>But given the instance head <code>UnitList (a ': as)</code>, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.</p></li></ul><p>After the <code>UnitList (a ': as)</code> instance is selected, GHC attempts to solve the constraints in the instance context, including the <code>a ~ ()</code> constraint. This <em>forces</em> <code>a</code> to be <code>()</code>, resolving the ambiguity and allowing type inference to proceed.</p><p>This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:</p><ul><li><p>If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.</p></li><li><p>But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell <em>you</em> what type this is,” effectively giving you a role in type inference itself.</p></li></ul><p>From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.</p><p>Given all of the above, consider again the definition of <code>IsEven</code> from earlier:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an <code>IsEven (a ': b0)</code> constraint, where <code>b0</code> is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an <code>IsEven '[a]</code> instance in the future.</p><p>To fix this, we can apply the same trick we used for <code>UnitList</code>, just in a slightly different way:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Again, the idea is to move the type information we <em>learn</em> from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can <strong>dramatically</strong> improve type inference in programs that make heavy use of TMP.</p><h3><a name="example-3-subtyping-constraints"></a>Example 3: Subtyping constraints</h3><p>At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at <a href="https://hasura.io/">Hasura</a>, I had the opportunity to design an internal parser combinator library that captures aspects of the <a href="https://graphql.org/">GraphQL</a> type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.</p><p>Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our <code>GQLKind</code> datatype has three cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLKind</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Both</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Input</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Output</span></code></pre><p>We use <code>DataKind</code>-promoted versions of this <code>GQLKind</code> type as a parameter to a <code>GQLType</code> GADT:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">TScalar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Both</span> +<span class="w"> </span><span class="kt">TInputObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">InputObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Input</span> +<span class="w"> </span><span class="kt">TIObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Output</span> +<span class="w"> </span><span class="c1">-- ...and so on...</span></code></pre><p>This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type <code>GQLType 'Input</code>, we can’t pass a <code>GQLType 'Both</code>, even though we really ought to be able to.</p><p>To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span></code></pre><p>The first case, <code>KRefl</code>, states that every kind is trivially a subkind of itself. The second case, <code>KBoth</code>, states that <code>Both</code> is a subkind of any kind at all. (This is a particularly literal example of <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">using a type to define axioms</a>.) The next step is to use TMP to implement proof inference:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KBoth</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span></code></pre><p>These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that <code>k</code> is a superkind of <code>'Input</code> or <code>'Output</code>, type inference will force them to be equal.</p><p>Using <code>IsSubKind</code>, we can easily resolve the problem described above. Rather than write a function with a type like this:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>…we simply use an <code>IsSubKind</code> constraint, instead:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now both <code>'Input</code> and <code>'Both</code> kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. <em>Consuming</em> the <code>SubKind</code> proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SelectionSet</span></code></pre><p>This type family is used to determine what a <code>GQLParser k a</code> actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>.</p><p>Fortunately, that is very easy to do using <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html">the <code>(:~:)</code> type from <code>Data.Type.Equality</code> in <code>base</code></a> to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">a</span></code></pre><p>Just as with any other GADT, <code>(:~:)</code> can be used to pack up type equalities and unpack them later; <code>a :~: b</code> just happens to be the GADT that corresponds precisely to the equality <code>a ~ b</code>. Using <code>(:~:)</code>, we can write a reusable proof that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>:</p><pre><code class="pygments"><span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="o">@</span><span class="kt">&#39;Input</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span></code></pre><p>This function is a very simple proof by cases, where <code>Refl</code> can be read as “Q.E.D.”:</p><ul><li><p>In the first case, matching on <code>KRefl</code> refines <code>k</code> to <code>'Input</code>, and <code>ParserInput 'Input</code> is <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li><li><p>Likewise, in the second case, matching on <code>KBoth</code> refines <code>k</code> to <code>'Both</code>, and <code>ParserInput 'Both</code> is also <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li></ul><p>This <code>inputParserInput</code> helper allows functions like <code>nullable</code>, which internally need <code>ParserInput k ~ InputValue</code>, to take the form</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nullable</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">inputParserInput</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ...implementation goes here... -}</span></code></pre><p>Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without <code>IsSubKind</code> doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!</p><h2><a name="wrapping-up-and-closing-thoughts"></a>Wrapping up and closing thoughts</h2><p>So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:</p><ul><li><p>Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.</p></li><li><p>Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.</p></li><li><p>Though I’ve called the technique “<em>typeclass</em> metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.</p></li><li><p>Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.</p></li></ul><p>The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.</p><p>Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.</p><p>It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:</p><ul><li><p>As mentioned earlier in this blog post, <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">the <code>GHC.Generics</code> module documentation</a> is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.</p></li><li><p>I have long believed that <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/">the GHC User’s Guide</a> is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.</p></li><li><p>Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the <a href="https://hackage.haskell.org/package/singletons">singletons</a> library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, <a href="https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf">the accompanying paper</a> is definitely worth a read if you’d like to go down that route.)</p></li></ul><p>Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.</p><ol class="footnotes"><li id="footnote-1"><p>Not to be confused with C++’s <a href="https://en.wikipedia.org/wiki/Template_metaprogramming"><em>template</em> metaprogramming</a>, though there are significant similarities between the two techniques. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>There have been proposals to introduce ordered instances, known in the literature as <a href="https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf"><em>instance chains</em></a>, but as of this writing, GHC does not implement them. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Note that this also preserves an important property of the Haskell type system, parametricity. A function like <code>id :: a -&gt; a</code> shouldn’t be allowed to do different things depending on which type is chosen for <code>a</code>, which our first version of <code>guardUnit</code> tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Short for <em>generalized algebraic datatypes</em>, which is a rather unhelpful name for actually understanding what they are or what they’re for. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for <code>IsEvenTF</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">exists</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">as&#39;</span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">)</span></code></pre><p>The type refinement provided by matching on <code>HCons</code> would be enough for the second case of <code>IsEvenTF</code> to be selected, which would provide an equality proof that <code>as</code> has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. <a href="#footnote-ref-5-1">↩</a></p></li><li id="footnote-6"><p>Actually, I’ve cheated a little bit here, because <code>unsingleton unitList</code> really does typecheck in GHCi under normal circumstances. That’s because <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules">the <code>ExtendedDefaultRules</code> extension</a> is enabled in GHCi by default, which defaults ambiguous type variables to <code>()</code>, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned <code>ExtendedDefaultRules</code> off to illustrate the problem. <a href="#footnote-ref-6-1">↩</a></p></li></ol></article>Names are not type safety2020-11-01T00:00:00Z2020-11-01T00:00:00ZAlexis King<article><p>Haskell programmers spend a lot of time talking about <em>type safety</em>. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published <a href="/blog/2019/11/05/parse-don-t-validate/">Parse, Don’t Validate</a> as an initial stab towards bridging that gap.</p><p>The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s <code>newtype</code> construct. The idea is simple enough—the <code>newtype</code> keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this <em>sounds</em> like a simple and straightforward path to type safety. For example, one might consider using a <code>newtype</code> declaration to define a type for an email address:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This technique can provide <em>some</em> value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct <em>kind</em> of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.</p><p>And names are not type safety.</p><h2><a name="intrinsic-and-extrinsic-safety"></a>Intrinsic and extrinsic safety</h2><p>To illustrate the difference between constructive data modeling (discussed at length in my <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">previous blog post</a>) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">One</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Two</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Three</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Four</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Five</span></code></pre><p>We could then write some functions to convert between <code>Int</code> and our <code>OneToFive</code> type:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">One</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Two</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Three</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Four</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Five</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">3</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">5</span></code></pre><p>This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="kt">Int</span></code></pre><p>Just as before, we can provide <code>toOneToFive</code> and <code>fromOneToFive</code> functions, with identical types:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">otherwise</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="p">(</span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">n</span></code></pre><p>If we put these declarations in their own module and choose not to export the <code>OneToFive</code> constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.</p><p>To see why, suppose we write a function that consumes a <code>OneToFive</code> value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"first"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"second"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"third"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fifth"</span></code></pre><p>The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an <code>Int</code>—after all, it <em>is</em> an <code>Int</code>. An <code>Int</code> can of course contain many other values besides <code>1</code> through <code>5</code>, so we are forced to add an error case to satisfy the exhaustiveness checker:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromOneToFive</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"first"</span> +<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"second"</span> +<span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"third"</span> +<span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fifth"</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: bad OneToFive value"</span></code></pre><p>In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:</p><ul><li><p>The constructive datatype captures its invariants in such a way that they are <em>accessible</em> to downstream consumers. This frees our <code>ordinal</code> function from worrying about handling illegal values, as they have been made unutterable.</p></li><li><p>The newtype wrapper provides a smart constructor that <em>validates</em> the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting <code>Int</code>s.</p></li></ul><p>Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of <code>error</code> has punched a hole right through our type system. If we were to add another constructor to our <code>OneToFive</code> datatype,<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> the version of <code>ordinal</code> that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.</p><p>All of this is a consequence of the fact that the constructive modeling is <em>intrinsically</em> type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent <code>6</code> using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an <code>Int</code>; its meaning is specified extrinsically via the <code>toOneToFive</code> smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.</p><h3><a name="revisiting-non-empty-lists"></a>Revisiting non-empty lists</h3><p>Our <code>OneToFive</code> datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the <code>NonEmpty</code> datatype I’ve repeatedly highlighted in recent blog posts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>It may be illustrative to imagine a version of <code>NonEmpty</code> represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Foldable</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Just as with <code>OneToFive</code>, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for <code>NonEmpty</code> was the ability to write a safe version of <code>head</code>, but the newtype version requires another assertion:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">x</span> +<span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines <code>NonEmpty</code>, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.</p><h2><a name="newtypes-as-tokens"></a>Newtypes as tokens</h2><p>If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes <em>can</em> provide a sort of safety, just a weaker one.</p><p>The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a <em>trust boundary</em> where internal invariants are enforced by restricting clients to a safe API.</p><p>We can use the <code>NonEmpty</code> example from above to illustrate how this works. We refrain from exporting the <code>NonEmpty</code> constructor, and we provide <code>head</code> and <code>tail</code> operations that we trust to never actually fail:</p><pre><code class="pygments"><span class="kr">module</span><span class="w"> </span><span class="nn">Data.List.NonEmpty.Newtype</span> +<span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">NonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">cons</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">nonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">head</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">tail</span> +<span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">cons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span> +<span class="nf">cons</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span> + +<span class="nf">tail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>Since the only way to construct or consume <code>NonEmpty</code> values is to use the functions in <code>Data.List.NonEmpty.Newtype</code>’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like <em>tokens</em>: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case <code>head</code> and <code>tail</code>, to obtain the values contained within.</p><p>This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid <code>NonEmpty []</code> value. For this reason, the newtype approach to type safety does not on its own constitute a <em>proof</em> that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a <code>Generic</code> instance for <code>NonEmpty</code>:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DeriveGeneric #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">GHC.Generics</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span></code></pre><p>However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">GHC</span><span class="o">.</span><span class="kt">Generics</span><span class="o">.</span><span class="n">to</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">K1</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>This is a particularly extreme example, since derived <code>Generic</code> instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived <code>Read</code> instance:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">read</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="s">"NonEmpty []"</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:</p><ul><li><p>All invariants must be made clear to maintainers of the trusted module. For simple types, such as <code>NonEmpty</code>, the invariant is self-evident, but for more sophisticated types, comments are not optional.</p></li><li><p>Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.</p></li><li><p>Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.</p></li><li><p>Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.</p></li></ul><p>In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.</p><p>In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.</p><h2><a name="other-newtype-use-abuse-and-misuse"></a>Other newtype use, abuse, and misuse</h2><p>The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:</p><ul><li><p>Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the <code>Sum</code> and <code>Product</code> newtypes from <code>Data.Monoid</code> provide useful <code>Monoid</code> instances for numeric types.</p></li><li><p>In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The <code>Flip</code> newtype from <code>Data.Bifunctor.Flip</code> is a simple example, flipping the arguments of a <code>Bifunctor</code> so the <code>Functor</code> instance may operate on the other side:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runFlip</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.</p></li><li><p>More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a <code>ByteString</code> containing a secret key may be wrapped in a newtype (with a <code>Show</code> instance omitted) to discourage code from accidentally logging or otherwise exposing it.</p></li></ul><p>All of these applications are good ones, but they have little to do with <em>type safety.</em> The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually <em>prevents</em> misuse; any part of the program may inspect the value at any time.</p><p>Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">unArgumentName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSONKey</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSONKey</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">Hashable</span><span class="p">,</span><span class="w"> </span><span class="kt">ToTxt</span><span class="p">,</span><span class="w"> </span><span class="kt">Lift</span><span class="p">,</span><span class="w"> </span><span class="kt">Generic</span><span class="p">,</span><span class="w"> </span><span class="kt">NFData</span><span class="p">,</span><span class="w"> </span><span class="kt">Cacheable</span><span class="w"> </span><span class="p">)</span></code></pre><p>This newtype is useless noise. Functionally, it is completely interchangeable with its underlying <code>Name</code> type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an <code>ArgumentName</code>, since the enclosing field name already makes its role clear.</p><p>Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:</p><ul><li><p>Primarily, types distinguish <em>functional</em> differences between values. A value of type <code>NonEmpty a</code> is <em>functionally</em> distinct from a value of type <code>[a]</code>, since it is fundamentally structurally different and permits additional operations. In this sense, types are <em>structural</em>; they describe what values <em>are</em> in the internal world of the programming language.</p></li><li><p>Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate <code>Distance</code> and <code>Duration</code> types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.</p></li></ul><p>Note that both these uses are <em>pragmatic</em>; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system <em>is</em> a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like <code>ArgumentName</code>.</p><p>If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use <code>Name</code>, but in situations where the different label adds genuine clarity, one can always use a type alias:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span></code></pre><p>Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.</p><h2><a name="final-thoughts-and-related-reading"></a>Final thoughts and related reading</h2><p>I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.</p><p>Newtypes are one particular mechanism of defining <em>wrapper types</em>, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.</p><p>The catalyst that got me to finally sit down and write this was the recently-published <a href="https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/">Tagged is not a Newtype</a>. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, <code>Tagged</code> <em>is</em> a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.</p><p>Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.</p><p>And a name is not type safety.</p><ol class="footnotes"><li id="footnote-1"><p>Admittedly rather unlikely given its name, but bear with me through the contrived example. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Types as axioms, or: playing god with static types2020-08-13T00:00:00Z2020-08-13T00:00:00ZAlexis King<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more open2020-01-19T00:00:00Z2020-01-19T00:00:00ZAlexis King<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Parse, don’t validate2019-11-05T00:00:00Z2019-11-05T00:00:00ZAlexis King<article><p>Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.</p><p>However, about a month ago, <a href="https://twitter.com/lexi_lambda/status/1182242561655746560">I was reflecting on Twitter</a> about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:</p><div style="text-align: center; font-size: larger"><strong>Parse, don’t validate.</strong></div><h2><a name="the-essence-of-type-driven-design"></a>The essence of type-driven design</h2><p>Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.</p><h3><a name="the-realm-of-possibility"></a>The realm of possibility</h3><p>One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:</p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Void</span></code></pre><p>Is it possible to implement <code>foo</code>? Trivially, the answer is <em>no</em>, as <code>Void</code> is a type that contains no values, so it’s impossible for <em>any</em> function to produce a value of type <code>Void</code>.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘head’: Patterns not matched: [] +</code></pre><p>This message is helpfully pointing out that our function is <em>partial</em>, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is <code>[]</code>, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.</p><h3><a name="turning-partial-functions-total"></a>Turning partial functions total</h3><p>To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the <code>head</code> function, and we’ll start with the simplest one.</p><h4><a name="managing-expectations"></a>Managing expectations</h4><p>As established, <code>head</code> is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the <code>Maybe</code> type:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span></code></pre><p>This buys us the freedom we need to implement <code>head</code>—it allows us to return <code>Nothing</code> when we discover we can’t produce a value of type <code>a</code> after all:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>Problem solved, right? For the moment, yes… but this solution has a hidden cost.</p><p>Returning <code>Maybe</code> is undoubtably convenient when we’re <em>implementing</em> <code>head</code>. However, it becomes significantly less convenient when we want to actually use it! Since <code>head</code> always has the potential to return <code>Nothing</code>, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">configDirsList</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">configDirsList</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">cacheDir</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="n">cacheDir</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"should never happen; already checked configDirs is non-empty"</span></code></pre><p>When <code>getConfigurationDirectories</code> retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use <code>head</code> in <code>main</code> to get the first element of the list, the <code>Maybe FilePath</code> result still requires us to handle a <code>Nothing</code> case that we know will never happen! This is terribly bad for several reasons:</p><ol><li><p>First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?</p></li><li><p>Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.</p></li><li><p>Finally, and worst of all, this code is a bug waiting to happen! What if <code>getConfigurationDirectories</code> were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update <code>main</code>, and suddenly the “impossible” error becomes not only possible, but probable.</p></li></ol><p>The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically <em>prove</em> the <code>Nothing</code> case impossible, then a modification to <code>getConfigurationDirectories</code> that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.</p><h4><a name="paying-it-forward"></a>Paying it forward</h4><p>Clearly, our modified version of <code>head</code> leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, <code>head</code> should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?</p><p>Let’s look at the original (partial) type signature for <code>head</code> again:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, <code>[a]</code>). Instead of weakening the return type, we can <em>strengthen</em> the argument type, eliminating the possibility of <code>head</code> ever being called on an empty list in the first place.</p><p>To do this, we need a type that represents non-empty lists. Fortunately, the existing <code>NonEmpty</code> type from <code>Data.List.NonEmpty</code> is exactly that. It has the following definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>Note that <code>NonEmpty a</code> is really just a tuple of an <code>a</code> and an ordinary, possibly-empty <code>[a]</code>. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the <code>[a]</code> component is <code>[]</code>, the <code>a</code> component must always be present. This makes <code>head</code> completely trivial to implement:<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Unlike before, GHC accepts this definition without complaint—this definition is <em>total</em>, not partial. We can update our program to use the new implementation:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">FilePath</span><span class="p">)</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">nonEmpty</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="p">)</span></code></pre><p>Note that the redundant check in <code>main</code> is now completely gone! Instead, we perform the check exactly once, in <code>getConfigurationDirectories</code>. It constructs a <code>NonEmpty a</code> from a <code>[a]</code> using the <code>nonEmpty</code> function from <code>Data.List.NonEmpty</code>, which has the following type:</p><pre><code class="pygments"><span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>The <code>Maybe</code> is still there, but this time, we handle the <code>Nothing</code> case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a <code>NonEmpty FilePath</code> value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type <code>NonEmpty a</code> as being like a value of type <code>[a]</code>, plus a <em>proof</em> that the list is non-empty.</p><p>By strengthening the type of the argument to <code>head</code> instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:</p><ul><li><p>The code has no redundant checks, so there can’t be any performance overhead.</p></li><li><p>Furthermore, if <code>getConfigurationDirectories</code> changes to stop checking that the list is non-empty, its return type must change, too. Consequently, <code>main</code> will fail to typecheck, alerting us to the problem before we even run the program!</p></li></ul><p>What’s more, it’s trivial to recover the old behavior of <code>head</code> from the new one by composing <code>head</code> with <code>nonEmpty</code>:</p><pre><code class="pygments"><span class="nf">head&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fmap</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">nonEmpty</span></code></pre><p>Note that the inverse is <em>not</em> true: there is no way to obtain the new version of <code>head</code> from the old one. All in all, the second approach is superior on all axes.</p><h3><a name="the-power-of-parsing"></a>The power of parsing</h3><p>You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:</p><pre><code class="pygments"><span class="nf">validateNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span> + +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="n">xs</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span></code></pre><p>These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: <code>validateNonEmpty</code> always returns <code>()</code>, the type that contains no information, but <code>parseNonEmpty</code> returns <code>NonEmpty a</code>, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but <code>parseNonEmpty</code> gives the caller access to the information it learned, while <code>validateNonEmpty</code> just throws it away.</p><p>These two functions elegantly illustrate two different perspectives on the role of a static type system: <code>validateNonEmpty</code> obeys the typechecker well enough, but only <code>parseNonEmpty</code> takes full advantage of it. If you see why <code>parseNonEmpty</code> is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of <code>parseNonEmpty</code>’s name. Is it really <em>parsing</em> anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe <code>parseNonEmpty</code> is a bona-fide parser (albeit a particularly simple one).</p><p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and <code>parseNonEmpty</code> is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.</p><p>Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:</p><ul><li><p>The <a href="https://hackage.haskell.org/package/aeson">aeson</a> library provides a <code>Parser</code> type that can be used to parse JSON data into domain types.</p></li><li><p>Likewise, <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> provides a set of parser combinators for parsing command-line arguments.</p></li><li><p>Database libraries like <a href="https://hackage.haskell.org/package/persistent">persistent</a> and <a href="https://hackage.haskell.org/package/postgresql-simple">postgresql-simple</a> have a mechanism for parsing values held in an external data store.</p></li><li><p>The <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.</p></li></ul><p>The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.</p><p>One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the <code>NonEmpty</code> example above: if the parsing and processing logic go out of sync, the program will fail to even compile.</p><h3><a name="the-danger-of-validation"></a>The danger of validation</h3><p>Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?</p><p>Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the <a href="http://langsec.org">language-theoretic security</a> field calls <em>shotgun parsing</em>. In the 2016 paper, <a href="http://langsec.org/papers/langsec-cwes-secdev2016.pdf">The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them</a>, its authors provide the following definition:</p><blockquote><p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.</p></blockquote><p>They go on to explain the problems inherent to such validation techniques:</p><blockquote><p>Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.</p></blockquote><p>In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.</p><p>It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.</p><p>Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p><h2><a name="parsing-not-validating-in-practice"></a>Parsing, not validating, in practice</h2><p>So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”</p><p>My advice: focus on the datatypes.</p><p>Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a <code>Map</code>. Adjust your function’s type signature to accept a <code>Map</code> instead of a list of tuples, and implement it as you normally would.</p><p>Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to <code>Map</code>, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of <code>checkNoDuplicateKeys</code>:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span></code></pre><p>Now the check <em>cannot</em> be omitted, since its result is actually necessary for the program to proceed!</p><p>This hypothetical scenario highlights two simple ideas:</p><ol><li><p><strong>Use a data structure that makes illegal states unrepresentable.</strong> Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.</p></li><li><p><strong>Push the burden of proof upward as far as possible, but no further.</strong> Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.</p></li></ol><p>In other words, write functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!</p><p>Here are a handful of additional points of advice, arranged in no particular order:</p><ul><li><p><strong>Let your datatypes inform your code, don’t let your code control your datatypes.</strong> Avoid the temptation to just stick a <code>Bool</code> in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.</p></li><li><p><strong>Treat functions that return <code>m ()</code> with deep suspicion.</strong> Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.</p></li><li><p><strong>Don’t be afraid to parse data in multiple passes.</strong> Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p></li><li><p><strong>Avoid denormalized representations of data, <em>especially</em> if it’s mutable.</strong> Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.</p><ul><li><p><strong>Keep denormalized representations of data behind abstraction boundaries.</strong> If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.</p></li></ul></li><li><p><strong>Use abstract datatypes to make validators “look like” parsers.</strong> Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract <code>newtype</code> with a smart constructor to “fake” a parser from a validator.</p></li></ul><p>As always, use your best judgement. It probably isn’t worth breaking out <a href="https://hackage.haskell.org/package/singletons">singletons</a> and refactoring your entire application just to get rid of a single <code>error "impossible"</code> call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.</p><h2><a name="recap-reflection-and-related-reading"></a>Recap, reflection, and related reading</h2><p>That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.</p><p>None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about <em>process</em>. My hope is that this is a small step in that direction.</p><p>Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post <a href="https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html">Type Safety Back and Forth</a>. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper <a href="https://kataskeue.com/gdp.pdf">Ghosts of Departed Proofs</a>, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.</p><p>As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit <em>any</em> value. These aren’t “real” values (unlike <code>null</code> in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that <a href="https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf">Fast and Loose Reasoning is Morally Correct</a>. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In fact, <code>Data.List.NonEmpty</code> already provides a <code>head</code> function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Rascal: a Haskell with more parentheses2017-01-02T00:00:00Z2017-01-02T00:00:00ZAlexis King<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Four months with Haskell2016-06-12T00:00:00Z2016-06-12T00:00:00ZAlexis King<article><p>At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.</p><p>Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an <em>incredible</em> programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.</p><h2><a name="dispelling-some-myths"></a>Dispelling some myths</h2><p>Before moving on and discussing my experiences in depth, I want to take a quick detour to dispel some frequent rumors I hear about why Haskell is at least potentially problematic. These are things I hear a <em>lot</em>, and nothing in my experience so far would lead me to believe these are actually true. Ultimately, I don’t want to spend too much time on these—I think that, for the most part, they are nitpicks that people complain about to avoid understanding the deeper and more insidious problems with the language—but I think it’s important to at least mention them.</p><h3><a name="hiring-haskell-developers-is-not-hard"></a>Hiring Haskell developers is not hard</h3><p>I am on the first Haskell team in my company, and I am among the first Haskell developers we ever hired. Not only were we hiring without much experience with Haskell at all, we explicitly <em>did not</em> want to hire remote. Debate all you like about whether or not permitting remote work is a good idea, but I don’t think anyone would dispute that this constraint makes hiring much harder. We didn’t have any trouble finding a very large stream of qualified applicants, and it definitely seems to have dispelled any fears that we would have trouble finding new candidates in the future.</p><h3><a name="performing-i-o-in-haskell-is-easy"></a>Performing I/O in Haskell is easy</h3><p>Haskell’s purity is a point of real contention, and it’s one of the most frustrating complaints I often hear about Haskell. It is surprisingly common to hear concerns along the lines of “I don’t want to use Haskell because its academic devotion to purity sounds like it would make it very hard to get anything done”. There are very valid reasons to avoid Haskell, but in practice, I/O is not one of them. In fact, I found that isolating I/O in Haskell was much the same as isolating I/O in every other language, which I need to do anyway to permit unit testing.</p><p>...you <em>do</em> write deterministic unit tests for your impure logic, right?</p><h3><a name="working-with-lots-of-monads-is-not-very-difficult"></a>Working with lots of monads is not very difficult</h3><p>The “M word” has ended up being a running joke <em>about</em> Haskell that actually ends up coming up fairly rarely <em>within</em> the Haskell community. To be clear, there is <em>no doubt</em> in my mind that monads make Haskell intimidating and provide a steep learning curve for new users. The proliferation of the joke that monads are impossible to explain, to the point of becoming mythologized, is absolutely indicative of a deeper problem about Haskell’s accessibility. However, once people learn the basics about monads, I’ve found that applying them is just as natural as applying any other programming pattern.</p><p>Monads are used to assist the programmer, not impede them, and they really do pay off in practice. When something has a monadic interface, there’s a decent chance I already know what that interface is going to do, and that makes working with lots of different monads surprisingly easy. Admittedly, I do rely very, very heavily on tooling to help me out here, but with things like mouseover type tooltips, I’ve actually found that working with a variety of different monads and monad transformers is actually quite pleasant, and it makes things very readable!</p><h2><a name="haskell-the-good-parts"></a>Haskell: the good parts</h2><p>With the disclaimers out of the way, I really just want to gush for a little bit. This is not going to be an objective, reasoned survey of why Haskell is good. I am not even really going to touch upon why types are so great and why purity is so wonderful—I’d love to discuss those in depth, but that’s for a different blog post. For now, I just want to touch upon the real surprises, the real things that made me <em>excited</em> about Haskell in ways I didn’t expect. These are the things that my subjective little experience has found fun.</p><h3><a name="language-extensions-are-haskell"></a>Language extensions <em>are</em> Haskell</h3><p>There was a time in my life when I spent a lot of time writing C. There are a lot of compilers for C, and they all implement the language in subtly different but often incompatible ways, especially on different platforms. The only way to maintain a modicum of predictability was to adhere to the standards <em>religiously</em>, even when certain GCC or MSVC extensions seem tantalizingly useful. I was actually bitten a few times by real instances where I figured I’d just use a harmless extension that was implemented everywhere, then found out it worked slightly differently across different compilers in a particular edge case. It was a learning experience.</p><p>It seems that this fear provides a very real distrust for using GHC’s numerous <em>language extensions</em>, and indeed, for a long time, I felt that it was probably an admirable goal to stick to Haskell 98 or Haskell 2010 as closely as possible. Sometimes I chose a slightly more verbose solution that was standard Haskell to avoid turning on a trivial extension that would make the code look a little bit cleaner.</p><p>About a year later, I’m finding that attitude was not only a mistake, but it forced me to often completely miss out on a lot of Haskell’s core value. GHC <em>won</em>, and now GHC and Haskell are basically synonymous. With that in mind, the portability concerns of language extensions are a bit of a non-issue, and turning them on is a very good idea! Some extensions are more than a little dangerous, so they cannot all be turned on without thinking, but the question is absolutely not “Is using language extensions a good idea?” and more “Is using <em>this</em> language extension a good idea?”</p><p>This is important, and I bring it up for a reason: so much of the awesomeness of Haskell is locked behind language extensions. Turning a lot of these on is one of the main things that made me really start to see how incredibly powerful Haskell actually is.</p><h3><a name="phantom-types"></a>Phantom types</h3><p>I’m going to start out by talking about <em>phantom types</em>, which are a pretty simple concept but a powerful one, and they serve as the foundation for a lot of other cool type-level tricks that can make Haskell extremely interesting. The basic idea of a phantom type is simple; it’s a type parameter that isn’t actually used to represent any particular runtime value:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type represents an id for some kind of value, but although the kind of value is specified in the type as the <code>a</code> type parameter, it isn’t actually used anywhere on the data definition—no matter what <code>a</code> is, an <code>Id</code> is just a piece of text. This makes it possible to write functions that operate on specific kinds of ids, and those invariants will be statically checked by the compiler, even though the runtime representation is entirely identical:</p><pre><code class="pygments"><span class="nf">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span></code></pre><p>Using <code>FlexibleInstances</code>, it’s also possible to create different instances for different kinds of ids. For example, it would be possible to have different <code>Show</code> instances depending on the type of id in question.</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"user #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"post #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span></code></pre><p>This provides a simple framework for encoding entirely arbitrary information into the type system, then asking the compiler to actually check assertions about that information. This is made even more powerful with some other extensions, which I’ll talk about shortly.</p><h3><a name="letting-the-compiler-write-code"></a>Letting the compiler write code</h3><p>One of the things I really dislike, more than most things, is boilerplate. A little bit of boilerplate is fine—even necessary at times—but as soon as I start wondering if a code generator would improve things, I think the programming language has pretty much failed me.</p><p>I write a lot of Racket because, in a sense, Racket is the ultimate boilerplate killer: the macro system is a first-class code generator integrated with the rest of the language, and it means that boilerplate is almost never an issue. Of course, that’s not always true: sometimes a bit of boilerplate <em>is</em> still necessary because macros cannot deduce enough information about the program to generate the code entirely on their own, and in Haskell, some of that information is actually present in the type system.</p><p>This leads to two absolutely incredible extensions, both of which are simple and related, but which actually <em>completely change</em> how I approach problems when programming. These two extensions are <code>GeneralizedNewtypeDeriving</code> and <code>StandaloneDeriving</code>.</p><h4><a name="newtypes-and-type-safety"></a>Newtypes and type safety</h4><p>The basic idea is that “newtypes” are just simple wrapper types in Haskell. This turns out to be extremely important when trying to find the value of Haskell because they allow you to harden type safety by specializing types to <em>your</em> domain. For example, consider a type representing a user’s name:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type is extremely simple, and in fact isn’t even at all different from a simple <code>Text</code> value with respect to its representation, since all combinations of unicode characters are allowed in a name. Therefore, what’s the point of a separate type? Well, this allows Haskell to introduce actual compilation failures when two different kinds of textual data are mixed. This is not a new idea, and even in languages that don’t support this sort of thing, Joel Spolsky’s old blog post <a href="http://www.joelonsoftware.com/articles/Wrong.html">Making Wrong Code Look Wrong</a> describes how it can be done by convention. Still, almost every modern language makes this possible: in C, it would be a single-member <code>struct</code>, in class-based OO languages, it would be a single-member class... this is not a complicated idea.</p><p>The difference lies in its usage. In other languages, this strategy is actually not very frequently employed for the simple reason that it is almost always extremely annoying. You are forced to do tons of wrapping/unwrapping, and at that point it isn’t really clear if you’re even getting all that much value out of the distinction when your first solution to a type mismatch is wrapping or unwrapping the value without a second thought. In Haskell, however, this can be heavily mitigated by asking the compiler to <em>automatically derive typeclass implementations</em>, which allow the unwrapping/wrapping to effectively happen implicitly for a constrained set of operations.</p><h4><a name="using-generalizednewtypederiving"></a>Using <code>GeneralizedNewtypeDeriving</code></h4><p>Consider the <code>Name</code> type once again, but this time, let’s derive a class:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">IsString</span><span class="p">)</span></code></pre><p>The <code>IsString</code> typeclass in Haskell allows custom types to automatically be created from string literals. It is <em>not</em> handled specially by Haskell’s <code>deriving</code> mechanism. Since <code>Text</code> implements <code>IsString</code>, an instance will be generated that simply defers to the underlying type, automatically generating the code to wrap the result up in a <code>Name</code> box at the end. This means that code like this will now just magically work:</p><pre><code class="pygments"><span class="nf">name</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Name</span> +<span class="nf">name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa P. Hacker"</span></code></pre><p>No boilerplate needs to be written! This is a neat trick, but it actually turns out to be far more useful than that simple example in practice. What really makes this functionality shine is when you want to derive <em>some</em> kinds of functionality but disallow some others. For example, using the <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a> package, it’s possible to do something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">ToText</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>This creates an opaque <code>Id</code> type, but it automatically generates conversions <em>to</em> textual formats. However, it does <em>not</em> automatically create <code>FromText</code> or <code>FromJSON</code> instances, which would be dangerous because decoding <code>Id</code>s can potentially fail. It’s then possible to write out those instances manually to preserve a type safety:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">FromText</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fromText</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="n">isValidId</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="kr">then</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">String</span><span class="w"> </span><span class="n">val</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">fromText</span><span class="w"> </span><span class="n">val</span><span class="p">)</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span></code></pre><h4><a name="using-standalonederiving"></a>Using <code>StandaloneDeriving</code></h4><p>The ordinary <code>deriving</code> mechanism is extremely useful, especially when paired with the above, but sometimes it is desirable to have a little bit more flexibility. In these cases, <code>StandaloneDeriving</code> can help.</p><p>Take the <code>Id</code> example again: it has a phantom type, and simply adding something like <code>deriving (ToText)</code> with derive <code>ToText</code> instances for <em>all</em> kinds of ids. It is potentially useful, however, to derive instances for more specific id types. Using standalone <code>deriving</code> constructs permits this sort of flexibility.</p><pre><code class="pygments"><span class="kr">deriving</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toText</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">postIdToText</span></code></pre><p>This is an example where GHC language extensions end up becoming significantly more than the sum of their parts, which seems to be a fairly frequent realization. The <code>StandaloneDeriving</code> mechanism is a little bit useful without <code>GeneralizedNewtypeDeriving</code>, but when combined, they are incredibly powerful tools for getting a very fine-grained kind of type safety <em>without</em> writing any boilerplate.</p><h3><a name="datakinds-are-super-cool-with-caveats"></a>DataKinds are super cool, with caveats</h3><p>Phantom types are quite wonderful, but they can only encode <em>types</em>, not arbitrary data. That’s where <code>DataKinds</code> and <code>KindSignatures</code> come in: they allow lifting arbitrary datatypes to the type level so that things that would normally be purely runtime values can be used at compile-time as well.</p><p>The way this works is pretty simple—when you define a datatype, you also define a “datakind”:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Registered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Anonymous</span></code></pre><p>Normally, the above declaration declares a <em>type</em>, <code>RegistrationStatus</code>, and two <em>data constructors</em>, <code>Registered</code> and <code>Anonymous</code>. With <code>DataKinds</code>, it also defines a <em>kind</em>, <code>RegistrationStatus</code>, and two <em>type constructors</em>, <code>Registered</code> and <code>Anonymous.</code></p><p>If that’s confusing, the way to understand that is to realize there is a sort of natural ordering here: types describe values, and kinds describe types. Therefore, turning on <code>DataKinds</code> “lifts” each definition by a single level, so types become kinds and values become types. This permits using these things at the type level:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>In this example, <code>UserId</code> still has a single phantom type variable, <code>s</code>, but this time it is constrained to the <code>RegistrationStatus</code> kind. Therefore, it can <em>only</em> be <code>Registered</code> or <code>Anonymous</code>. This cooperates well with the aforementioned <code>StandaloneDeriving</code> mechanism, and it mostly provides a convenient way to constrain type variables to custom kinds.</p><p>In general, <code>DataKinds</code> is a much more powerful extension, allowing things like type-level natural numbers or strings, which can be used to perform actual type-level computation (especially in combination with <code>TypeFamilies</code>) or a sort of metaprogramming. In some cases, they can even be used to implement things emulating things you can do with dependent types.</p><p>I think <code>DataKinds</code> are a very cool Haskell extension, but there are a couple caveats. One of the main ones is how new kinds are defined: <code>DataKinds</code> “hijacks” the existing datatype declaration syntax by making every single datatype declaration define a type <em>and</em> a kind. This is a little confusing, and it would be nice if a different syntax was used so that each could be defined independently.</p><p>Similarly, it seems that a lot of work is being done to allow using runtime values at the type level, but I wonder if people will ever need to use, say, runtime values at the <em>kind</em> level. This immediately evokes thoughts of Racket’s phase-based macro system, and I wonder if some of this duplication would be unnecessary with something similar.</p><p>Food for thought, but overall, <code>DataKinds</code> are a very nice addition to help with precisely and specifically typing particular problems.</p><h3><a name="typeclasses-can-emulate-effects"></a>Typeclasses can emulate effects</h3><p>This is something that I’ve found interesting in my time writing Haskell because I have <em>no idea</em> if it’s idiomatic or not, but it seems pretty powerful. The initial motivator for this idea was figuring out how to test our code without constantly dropping into <code>IO</code>.</p><p>More generally, we wanted to be able to unit test by “mocking” out collaborators, as it would be described in object oriented programming. I was always semi-distrustful of mocking, and indeed, it seems likely that it is heavily abused in certain circles, but I’ve come to appreciate the need that sometimes it is important to stub things out, <em>even in pure code</em>.</p><p>As an example, consider some code that needs access to the current time. This is something that would normally require <code>IO</code>, but we likely want to be able to use the value in a pure context without “infecting” the entire program with <code>IO</code> types. In Haskell, I have generally seen three ways of handling this sort of thing:</p><ol><li><p>Just inject the required values into the function and produce them “higher up” where I/O is okay. If threading the value around becomes too burdensome, use a Reader monad.</p></li><li><p>Use a free monad or similar to create a pure DSL of sorts, then write interpreters for various implementations, one of which uses <code>IO</code>.</p></li><li><p>Create custom monadic typeclasses that provide interfaces to the functionality you want to perform, then create instances, one of which is an instance over <code>IO</code>.</p></li></ol><p>This last approach seems to be less common in Haskell, but it’s the approach we took, and it seems to work out remarkably well. Returning to the need to get the current time, we could pretty easily write such a typeclass to encode that need:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">UTCTime</span></code></pre><p>Now we can write functions that use the current time:</p><pre><code class="pygments"><span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span><span class="p">)</span></code></pre><p>Now, we can write instances for <code>CurrentTime</code> that will allow us to run the same code in different contexts:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runAppM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">runTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">runTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftIO</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Time</span><span class="o">.</span><span class="kt">Clock</span><span class="o">.</span><span class="n">getCurrentTime</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">posixSecondsToUTCTime</span><span class="w"> </span><span class="mi">0</span></code></pre><p>Where this really starts to shine is when adding additional effects. For example, the above token validation function might also need information about some kind of secret used for signing. Under this model, it’s just another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getTokenSecret</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Secret</span> + +<span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">secret</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getTokenSecret</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span> +<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">verifySignature</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="n">secret</span><span class="p">)</span></code></pre><p>Of course, so far all of these functions have been extremely simple, and we’ve basically been using them as a glorified reader monad. In practice, though, we use this pattern for lots more than just retrieving values. For example, we might have a typeclass for database interactions:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> +<span class="w"> </span><span class="n">insertUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">PersistenceError</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">))</span></code></pre><p>With all of this done, it becomes incredibly easy to see which functions are using which effects:</p><pre><code class="pygments"><span class="nf">postUsers</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">postUsers</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">getHealthcheck</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">getHealthcheck</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>There’s no need to perform any lifting, and this all seems to scale quite nicely. We’ve written some additional utilities to help write tests against functions using these kinds of monadic interfaces, and even though there’s a little bit of annoying boilerplate in a few spots, overall it seems to work quite elegantly.</p><p>I’m not entirely sure how common this is in the Haskell community, but it’s certainly pretty neat how easy it is to get nearly all of the benefits of effect types in other languages simply by composing some of Haskell’s simplest features.</p><h3><a name="atom-s-ide-haskell-tooling-is-invaluable"></a>Atom’s ide-haskell tooling is invaluable</h3><p>Alright, so, confession time: I don’t use Emacs.</p><p>I know, I know, how is that possible? I write Lisp, after all. Well, honestly, I tried picking it up a number of times, but none of those times did I get far enough to ditch my other tools. For Racket work, I use DrRacket, but for almost everything else, I use Atom.</p><p>Atom has a lot of flaws, but it’s also pretty amazing in places, and I absolutely <em>love</em> the Haskell tooling written by the wonderful <a href="https://github.com/atom-haskell">atom-haskell</a> folks. I use it constantly, and even though it doesn’t always work perfectly, it works pretty well. When it has problems, I’ve at least figured out how to get it working correctly.</p><p>This is probably hard to really explain without seeing it for yourself, but I’ve found that I basically <em>depend</em> on this sort of tooling to be fully productive in Haskell, and I have no problem admitting that. The ability to get instant feedback about type errors tied to visual source locations, to be able to directly manipulate the source by selecting expressions and getting type information, and even the option to get inline linter suggestions means I spend a lot less time glancing at the terminal, and even less time in the REPL.</p><p>The tooling is far from perfect, and it leaves a lot to be desired in places (the idea of using that static information for automated, project-wide refactoring <em>a la</em> Java is tantalizing), but most of those things are ideas of what amazing things could be, not broken or missing essentials. I am pretty satisfied with ide-haskell right now, and I can only hope it continues to get better and better.</p><h2><a name="frustrations-drawbacks-and-pain-points"></a>Frustrations, drawbacks, and pain points</h2><p>Haskell is not perfect. In fact, far from it. There is a vast array of little annoyances that I have with the language, as is the case with any language. Still, there are a few overarching problems that I would really like to at least mention. These are the biggest sources of frustration for me so far.</p><h3><a name="purity-failure-and-exception-handling"></a>Purity, failure, and exception-handling</h3><p>One of Haskell’s defining features is its purity—I don’t think many would disagree with that. Some people consider it a drawback, others consider it one of its greatest boons. Personally, I like it a lot, and I think one of the best parts about it is how it requires the programmer to be incredibly deliberate about failure.</p><p>In many languages, when looking up a value from a container where the key doesn’t exist, there are really two ways to go about expressing this failure:</p><ol><li><p>Throw an exception.</p></li><li><p>Return <code>null</code>.</p></li></ol><p>The former is scary because it means <em>any</em> call to any function can make the entire program blow up, and it’s often impossible to know which functions even have the potential to throw. This creates a certain kind of non-local control flow that can sometimes cause a lot of unpredictability. The second option is much the same, especially when any value in a program might be <code>null</code>; it just defers the failure.</p><p>In languages with option types, this is somewhat mitigated. Java now has option types, too, but they are still frequently cumbersome to use because there is nothing like monads to use to simply chain operations together. Haskell, in comparison, has an incredible complement of tools to simply handle errors without a whole lot of burden on the programmer, and I have found that, in practice, this is <em>actually helpful</em> and I really do write better error-handling code.</p><h4><a name="first-the-good-parts"></a>First, the good parts</h4><p>I have seen a comparison drawn between throwing checked exceptions and returning <code>Maybe</code> or <code>Either</code> types, but in practice the difference is massive. Handling checked exceptions is a monotonous chore because they are not first-class values, they are actually entirely separate linguistic constructs. Consider a library that throws a <code>LibraryException</code>, and you want to wrap that library and convert those exceptions to <code>ApplicationException</code>s. Well, have fun writing this code dozens of times:</p><pre><code class="pygments"><span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomething</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span> + +<span class="c1">// ...</span> + +<span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomethingElse</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span></code></pre><p>In Haskell, failure is just represented by first-class values, and it’s totally possible to write helper functions to abstract over that kind of boilerplate:</p><pre><code class="pygments"><span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ApplicationError</span> +<span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">ApplicationError</span><span class="w"> </span><span class="n">a</span> +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapLeft</span><span class="w"> </span><span class="n">libraryToApplication</span></code></pre><p>Now, that same boilerplate-y code becomes nearly invisible:</p><pre><code class="pygments"><span class="nf">x</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomething</span> + +<span class="c1">-- ...</span> + +<span class="nf">y</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomethingElse</span></code></pre><p>This might not <em>seem</em> like much, but it really cuts down on the amount of visual noise, which ends up making all the difference. Boilerplate incurs a cost much bigger than simply taking the time to type it all out (though that’s important, too): the cognitive overhead of parsing which parts of a program are boilerplate has a significant impact on readability.</p><h4><a name="so-what-s-the-problem"></a>So what’s the problem?</h4><p>If error handling is so great in Haskell, then why am I putting it under the complaints section? Well, it turns out that not everyone seems to think it’s as great as I make it out to be because people seem to keep writing Haskell APIs that throw exceptions!</p><p>Despite what some purists would have you believe, Haskell has exceptions, and they are not uncommon. Lots of things can throw exceptions, some of which are probably reasonable. Failing to connect to a database is a pretty catastrophic error, so it seems fair that it would throw. On the other hand, inserting a duplicate record is pretty normal operation, so it seems like that should <em>not</em> throw.</p><p>I mostly treat exceptions in Haskell as unrecoverable catastrophes. If I throw an error in <em>my</em> code, I do not intend to catch it. That means something horrible happened, and I just want that horribleness to show up in a log somewhere so I can fix the problem. If I care about failure, there are better ways to handle that failure gracefully.</p><p>It’s also probably worth noting that exceptions in Haskell can be thrown from anywhere, even pure code, but can only be <em>caught</em> within the <code>IO</code> monad. This is especially scary, but I’ve seen it happen in actual libraries out in the wild, even ones that the entire Haskell ecosystem is built on. One of the crowning examples of this is the <code>text</code> package, which provides a function called <code>decodeUtf8</code> to convert bytestrings into text. Its type is very simple:</p><pre><code class="pygments"><span class="nf">decodeUtf8</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ByteString</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>But wait, what if the bytestring is not actually a valid UTF-8 string?</p><p>Boom. There goes the application.</p><p>Okay, okay, well, at least the <code>text</code> package provides another function, this one called <code>decodeUtf8'</code>, which returns an <code>Either</code>. This is good, and I’ve trained myself to only ever use <code>decodeUtf8'</code>, but it still has some pretty significant problems:</p><ul><li><p>The <em>safe</em> version of this function is the “prime” version, rather than the other way around, which encourages people to use the unsafe one. Ideally, the unsafe one should be explicitly labeled as such... maybe call it <code>unsafeDecodeUtf8</code>?</p></li><li><p>This is not a hypothetical problem. When using a Haskell JWT library, we found a function that converts a string into a JWT. Since not all strings are JWTs, the library intelligently returns a <code>Maybe</code>. Therefore, we figured we were safe.</p><p>A couple weeks later, we found that providing this function with invalid data was returning HTTP 500 errors. Why? Our error handling was meticulous! Well, the answer was a <code>decodeUtf8</code> call, hidden inside of the JWT library. This is especially egregious, given that the API it exposed returned a <code>Maybe</code> anyway! It would have been trivial to use the safe version there, instead, but the poor, misleading name led the library developer to overlook the bug lurking in the otherwise innocuous function.</p><p>Even worse, this function was totally pure, and we used it in pure code, so we could not simply wrap the function and catch the exception. We had two options: use <code>unsafePerformIO</code> (yuck!) or perform a check before handing the data to the buggy function. We chose the latter, but in some cases, I imagine that could be too difficult to do in order to make it feasible.</p></li></ul><p>The point I’m trying to make is that this is a real problem, and it seems to me that throwing exceptions invalidates one of the primary advantages of Haskell. It disappointed me to realize that a significant amount of code written by FP Complete, one of the primary authors of some of the most important “modern Haskell” code in existence (including Stack), seem to very frequently expose APIs that will throw.</p><p>I’m not sure how much of this stems from a fundamental divide in the Haskell ecosystem and how much it is simply due to Michael Snoyman’s coding style, given that he is the primary author of a number of these tools and libraries that seem very eager to throw exceptions. As just one example of a real situation in which we were surprised by this behavior, we used Snoyman’s http-client library and found that it mysteriously throws upon nearly <em>any</em> failure state:</p><blockquote><p>A note on exceptions: for the most part, all actions that perform I/O should be assumed to throw an <code>HttpException</code> in the event of some problem, and all pure functions will be total. For example, <code>withResponse</code>, <code>httpLbs</code>, and <code>BodyReader</code> can all throw exceptions.</p></blockquote><p>This doesn’t seem entirely unreasonable—after all, isn’t a failure to negotiate TLS fairly catastrophic?—until you consider our use case. We needed to make a subrequest during the extent of another HTTP request to our server, and if that subrequest fails, we absolutely need to handle that failure gracefully. Of course, this is not <em>terrible</em> given that we are in <code>IO</code> so we can actually catch these exceptions, but since this behavior was only noted in a single aside at the top of the documentation, we didn’t realize we were forgetting error handling until far too late and requests were silently failing.</p><p>Exceptions seem to devalue one of the most powerful concepts in Haskell: if I don’t consider all the possibilities, my code <em>does not compile</em>. In practice, when working with APIs that properly encode these possibilities into the type system, this value proposition seems to be real. I really do find myself writing code that works correctly as soon as it compiles. It’s almost magical.</p><p>Using exceptions throws that all out the window, and I wish the Haskell ecosystem was generally more cautious about when to use them.</p><h3><a name="the-string-problem"></a>The String problem</h3><p>I sort of alluded to this a tiny bit in the last section, and that is probably indicative of how bad this issue is. I’m just going to be blunt: <strong>In Haskell, strings suck.</strong></p><p>This is always a bit of an amusing point whenever it is discussed because of how silly it seems. Haskell is a research language with a cutting-edge type system and some of the fanciest features of any language in existence. When everyday programming might use things like “profunctors”, “injective type families”, and “generalized algebraic datatypes”, you would think that dealing with <em>strings</em> would be a well-solved problem.</p><p>But it isn’t. Haskell libraries frequently use not one, not two, but <em><strong>five</strong></em> kinds of strings. Let’s list them off, shall we?</p><ul><li><p>First off, there’s the built-in <code>String</code> type, which is actually an alias for the <code>[Char]</code> type. For those not intimately familiar with Haskell, that’s a <em>linked list of characters</em>. As <a href="http://www.stephendiehl.com/">Stephen Diehl</a> recently put it in <a href="http://www.stephendiehl.com/posts/strings.html">a blog post describing the disaster that is Haskell string types</a>:</p><blockquote><p>This is not only a bad representation, it’s quite possibly the least efficient (non-contrived) representation of text data possible and has horrible performance in both time and space. <em>And it’s used everywhere in Haskell.</em></p></blockquote><p>The point is, it’s really bad. This type is not a useful representation for textual data in practical applications.</p></li><li><p>Moving on, we have a fairly decent type, <code>Text</code>, which comes from <code>Data.Text</code> in the <code>text</code> package. This is a decent representation of text, and it’s probably the one that everything should use. Well, maybe. Because <code>Text</code> comes in two varieties: lazy and strict. Nobody seems to agree on which of those two should be used, though, and they are totally incompatible types: functions that work with one kind of text won’t work with the other. You have to manually convert between them.</p></li><li><p>Finally, we have <code>ByteString</code>, which is horribly misnamed because it really isn’t a string at all, at least not in the textual sense. A better name for this type would have simply been <code>Bytes</code>, which sounds a lot scarier. And that would be good, because data typed as a <code>ByteString</code> is as close as you can get in Haskell to not assigning a type at all: a bytestring holds arbitrary bytes without assigning them any meaning whatsoever!</p><p>Or at least, that’s the intention. The trouble is that people <em>don’t</em> treat bytestrings like that—they just use them to toss pieces of text around, even when those pieces of text have a well-defined encoding and represent textual data. This leads to the <code>decodeUtf8</code> problem mentioned above, but it’s bigger than that because it often ends up with some poor APIs that assign some interpretation to <code>ByteString</code> data without assigning it a different type.</p><p>Again, this is throwing away so much of Haskell’s safety. It would be like using <code>Int</code> to keep track of boolean data (“just use 0 and 1!”) or using empty and singleton lists instead of using <code>Maybe</code>. When you use the precise type, you encode invariants and contracts into statically-checked assertions, but when you use general types like <code>ByteString</code>, you give that up.</p><p>Oh, and did I mention that <code>ByteString</code>s also come in incompatible lazy and strict versions, too?</p></li></ul><p>So, obviously, the answer is to just stop using the bad types and to just use (one kind of) <code>Text</code> everywhere. Great! Except that the other types are totally inescapable. The entire standard library uses <code>String</code> exclusively—after all, <code>text</code> is a separate package—and small libraries often use <code>String</code> instead of <code>text</code> because they have no need to bring in the dependency. Of course, this just means every real application pays the performance hit of converting between all these different kinds of strings.</p><p>Similarly, those that <em>do</em> use <code>Text</code> often use different kinds of text, so code ends up littered with <code>fromStrict</code> or <code>toStrict</code> coercions, which (again) have a cost. I’ve already ranted enough about <code>ByteString</code>, but basically, if you’re using <code>ByteString</code> in your API to pass around data that is semantically text, you are causing me pain. Please stop.</p><p>It seems that the way <code>Data.Text</code> probably <em>should</em> have been designed was by making <code>Text</code> a typeclass, then making the lazy and strict implementations instances of that typeclass. Still, the fact that both of them exist would always cause problems. I’m actually unsure which one is the “correct” choice—I don’t know enough about how the two perform in practice—but it seems likely that picking <em>either</em> one would be a performance improvement over the current system, which is constantly spending time converting between the two.</p><p>This issue has been ranted about plenty, so I won’t ramble on, but if you’re designing new libraries, please, <em>please</em> use <code>Text</code>. Your users will thank you.</p><h3><a name="documentation-is-nearly-worthless"></a>Documentation is nearly worthless</h3><p>Finally, let’s talk about documentation.</p><p>One of my favorite programming languages is Racket. Racket has a documentation tool called Scribble. Scribble is special because it is a totally separate domain-specific language for writing documentation, and it makes it fun and easy to write good explanations. There are even forms for typesetting automatically-rendered examples that look like a REPL. If the examples ever break or become incorrect, the docs don’t even compile.</p><p>All of the Racket core library documentation makes sure to set a good example about what good documentation should look like. The vast majority of the documentation is paragraphs of prose and simple but practical examples. There are also type signatures (in the form of contracts), and those are super important, but they are so effective because of how the prose explains what each function does, when to use it, <em>why</em> you’d use it, and <em>why you wouldn’t use it</em>.</p><p>Everything is cross-referenced automatically. The documentation is completely searchable locally out of the box. As soon as you install a package, its docs are automatically indexed. User-written libraries tend to have pretty good docs, too, because the standard libraries set such a good example <em>and</em> because the tools are so fantastic. Racket docs are really nice, and they’re so good they actually make things like Stack Overflow or even Google mostly irrelevant. It’s all there in the manual.</p><p>Haskell documentation is the opposite of everything I just said.</p><ul><li><p>The core libraries are poorly documented. Most functions include a sentence of description, and almost none include examples. At their worst, the descriptions simply restate the type signature.</p></li><li><p>Third-party libraries’ documentation is even worse, going frequently completely undocumented and actually only including type signatures and nothing else.</p></li><li><p>Haddock is an incredibly user-hostile tool for writing anything other than tiny snippets of documentation and is not very good at supporting prose. Notably, Haddock’s documentation is not generated using Haddock (and it still manages to be almost unusable). Forcing all documentation into inline comments makes users unlikely to write much explanation, and there is no ability for abstraction.</p></li><li><p>Reading documentation locally is very difficult because there is no easy way to open documentation for a particular package in a web browser, and it’s <em>certainly</em> not searchable. This is especially ridiculous given that Hoogle exists, which is one of best ways to search API docs in existence. There should be a <code>stack hoogle</code> command that just opens a Hoogle page for all locally-installed packages and Just Works, but there isn’t.</p></li><li><p>Most valuable information exists outside of documentation, so Google becomes a go-to immediately after a quick glance at the docs, and information is spread across blog posts, mailing lists, and obscure reddit posts.</p></li></ul><p>This is a problem that cannot be fixed by just making Haddock better, nor can it be fixed simply by improving the existing standard library documentation. There is a fundamental problem with Haskell documentation (which, to be completely fair, is not unique to Haskell), which is that its tools do not support anything more than API docs.</p><p>Good documentation is so much more than “here’s what this function does”; it’s about guides and tutorials and case studies and common pitfalls. <a href="http://docs.racket-lang.org/lens/lens-guide.html">This is documentation for someone new to lenses.</a> <a href="https://hackage.haskell.org/package/lens#readme">This is not.</a> Take note of the difference.</p><h2><a name="conclusion-and-other-thoughts"></a>Conclusion and other thoughts</h2><p>Haskell is an incredible programming platform, and indeed, it is sometimes mind-boggling how complete it is. It also has a lot of rough edges, sometimes in places that feel like they need a lot more care, or perhaps they’re even simply unfinished.</p><p>I could spend weeks writing about all the things I really like or dislike about the language, discussing in fine detail all the things that have made me excited or all the little bits that have made me want to tear my hair out. Heck, I could probably spend a month writing about strings alone. That’s not the point, though... I took a risk with Haskell, and it’s paid off. I’m not yet sure exactly how I feel about it, or when I would chose it relative to other tools, but it is currently very high on my list of favorite technologies.</p><p>I did not come to Haskell with a distaste for static typing, despite the fact that I write so much Racket, a dynamically typed language (by default, at least). I don’t really use Typed Racket, and despite my love for Haskell and its type system, I am not sure I will use much more of it than I did before. Haskell and Racket are very different languages, which is justified in some places and probably sort of circumstantial in others.</p><p>The future of Haskell seems bright, and a lot of the changes in the just-released GHC 8 are extremely exciting. I did not list records as a pain point because the changes in GHC 8 appear to make them a <em>lot</em> more palatable, although whether or not they solve that problem completely remains to be seen. I will absolutely continue to write Haskell and push it to its limits where I can, and hopefully try and take as much as I can from it along the way.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/functional-programming.rss.xml b/feeds/functional-programming.rss.xml new file mode 100644 index 0000000..81b6f91 --- /dev/null +++ b/feeds/functional-programming.rss.xml @@ -0,0 +1,653 @@ +Posts tagged ‘functional programming’ | Alexis King’s BlogPosts tagged ‘functional programming’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/functional-programming.html25 Mar 202125 Mar 202160An introduction to typeclass metaprogramminghttps://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/https://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/25 Mar 2021<article><p><em>Typeclass metaprogramming</em> is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem), and it is the core mechanism used to implement generic programming via <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">GHC generics</a>. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.</p><p>This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does <em>not</em> attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also <em>not</em> a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.</p><h2><a name="part-1-basic-building-blocks"></a>Part 1: Basic building blocks</h2><p>Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.</p><p>To start, we’ll cover the absolute foundations of typeclass metaprogramming.</p><h3><a name="typeclasses-as-functions-from-types-to-terms"></a>Typeclasses as functions from types to terms</h3><p>As its name implies, typeclass metaprogramming (henceforth TMP<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup>) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic <code>==</code> operator via the <code>Eq</code> class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: <strong>typeclasses are functions from types to (runtime) terms</strong>.</p><p>What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called <code>TypeOf</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Given these instances, we can observe that they do what we expect in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>Note that both the <code>TypeOf Bool</code> and <code>TypeOf Char</code> instances ignore the argument to <code>typeOf</code> altogether. This makes sense, as the whole point of the <code>TypeOf</code> class is to get access to <em>type</em> information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This typeclass definition is a little unusual, as the type parameter <code>a</code> doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>the full type of the <code>show</code> method is implicitly extended with a <code>Show a</code> constraint to yield:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Furthermore, if we write <code>forall</code>s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the <em>full</em> type of <code>show</code>:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>In the same vein, we can write out the full type of <code>typeOf</code>, as given by our new definition of <code>TypeOf</code>:</p><pre><code class="pygments"><span class="nf">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This type is still unusual, as the <code>a</code> type parameter doesn’t appear anywhere to the right of the <code>=&gt;</code> arrow. This makes the type parameter trivially <em>ambiguous</em>, which is to say it’s impossible for GHC to infer what <code>a</code> should be at any call site. Fortunately, <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html">we can use <code>TypeApplications</code></a> to pass a type for <code>a</code> directly, as we can see in the updated definition of <code>TypeOf (a, b)</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Once again, we can test out our new definitions in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="kt">Bool</span> +<span class="s">"Bool"</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Bool</span><span class="p">,</span><span class="w"> </span><span class="kt">Char</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our <code>typeOf</code> function is, quite literally, a function that accepts a single type as an argument and returns a term-level <code>String</code>. Of course, the <code>TypeOf</code> typeclass is not a particularly <em>useful</em> example of such a function, but it demonstrates how easy it is to construct.</p><h3><a name="type-level-interpreters"></a>Type-level interpreters</h3><p>One important consequence of eliminating the value-level argument of <code>typeOf</code> is that there is no need for its argument type to actually be <em>inhabited</em>. For example, consider the <code>TypeOf</code> instance on <code>Void</code> from <code>Data.Void</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Void</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Void"</span></code></pre><p>This above instance is no different from the ones on <code>Bool</code> and <code>Char</code> even though <code>Void</code> is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of <code>TypeOf</code> on lists:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"["</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"]"</span></code></pre><p>If <code>typeOf</code> required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type <code>a</code> to recursively apply <code>typeOf</code> to. But since <code>typeOf</code> only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.</p><p>A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Z</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="n">a</span></code></pre><p>It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:</p><ul><li><p><code>Z</code> is a type that represents 0.</p></li><li><p><code>S Z</code> is a type that represents 1.</p></li><li><p><code>S (S Z)</code> is a type that represents 2.</p></li></ul><p>And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Numeric.Natural</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>As its name implies, <code>reifyNat</code> reifies a type-level natural number encoded using our datatypes above into a term-level <code>Natural</code> value:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="kt">Z</span> +<span class="mi">0</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span> +<span class="mi">1</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="mi">2</span></code></pre><p>One way to think about <code>reifyNat</code> is as an <em>interpreter</em> of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.</p><h3><a name="overlapping-instances"></a>Overlapping instances</h3><p>Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for <code>Show (Maybe a)</code>, you aren’t supposed to <em>also</em> write an instance for <code>Show (Maybe Bool)</code>, since it isn’t clear whether <code>show (Just True)</code> should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.</p><p>Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.</p><p>As a simple example, suppose we wanted to write a typeclass that checks whether a given type is <code>()</code> or not:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:</p><pre><code class="pygments"><span class="c1">-- not actually valid Haskell, just an example</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>But if we try to translate this to typeclass instances, we’ll get a problem:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> This means GHC will complain about instance overlap if we try to evaluate <code>isUnit @()</code>:</p><pre><code>ghci&gt; isUnit @() + +error: + • Overlapping instances for IsUnit () + arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance IsUnit () +</code></pre><p>To fix this, we have to explicitly mark <code>IsUnit ()</code> as overlapping:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>Now GHC accepts the expression without complaint:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="nb">()</span> +<span class="kt">True</span></code></pre><p>What does the <code>{-# OVERLAPPING #-}</code> pragma do, exactly? The gory details are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances">spelled out in the GHC User’s Guide</a>, but the simple explanation is that <code>{-# OVERLAPPING #-}</code> relaxes the overlap checker as long as the instance is <em>strictly more specific</em> than the instance(s) it overlaps with. In this case, that is true: <code>IsUnit ()</code> is trivially more specific than <code>IsUnit a</code>, since the former only matches <code>()</code> while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.</p><p>Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>The intent of <code>guardUnit</code> is to use <code>isUnit</code> to detect if its argument is of type <code>()</code>, and if it is, to return an error. However, even though we marked <code>IsUnit ()</code> overlapping, we still get an overlapping instance error:</p><pre><code>error: + • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance [overlapping] IsUnit () + • In the expression: isUnit @a +</code></pre><p>What gives? The problem is that GHC simply doesn’t know what type <code>a</code> is when compiling <code>guardUnit</code>. It <em>could</em> be instantiated to <code>()</code> where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.</p><p>This behavior is actually a very, very good thing. If GHC were to blindly pick the <code>IsUnit a</code> instance in this case, then <code>guardUnit</code> would always take the <code>False</code> branch, even when passed a value of type <code>()</code>! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when <code>{-# OVERLAPPING #-}</code> annotations are used, so it’s important to keep their limitations in mind.</p><p>As it happens, in this particular case, the error is easily remedied. We simply have to add an <code>IsUnit</code> constraint to the type signature of <code>guardUnit</code>:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now picking the right <code>IsUnit</code> instance is deferred to the place where <code>guardUnit</code> is used, and the definition is accepted.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><h3><a name="type-families-are-functions-from-types-to-types"></a>Type families are functions from types to types</h3><p>In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE TypeFamilies #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>The above is a <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families">closed type family</a>, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of <code>Sum</code> would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="nf">sum</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span> +<span class="nf">sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="nf">sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="n">sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.</p><p>To test our definition of <code>Sum</code> in GHCi, we can use <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind">the <code>:kind!</code> command</a>, which prints out a type and its kind after reducing it as much as possible:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span></code></pre><p>We can also combine <code>Sum</code> with our <code>ReifyNat</code> class from earlier:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.</p><h3><a name="example-1-generalized-concat"></a>Example 1: Generalized <code>concat</code></h3><p>Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a <code>flatten</code> function similar to like-named functions provided by many dynamically-typed languages. In those languages, <code>flatten</code> is like <code>concat</code>, but it works on a list of arbitrary depth. For example, we might use it like this:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]</span></code></pre><p>In Haskell, lists of different depths have different types, so multiple levels of <code>concat</code> have to be applied explicitly. But using TMP, we can write a generic <code>flatten</code> function that operates on lists of any depth!</p><p>Since this is <em>typeclass</em> metaprogramming, we’ll unsurprisingly begin with a typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="o">???</span><span class="p">]</span></code></pre><p>Our first challenge is writing the return type of <code>flatten</code>. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>Now we can write our <code>Flatten</code> instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>The inductive case is when the type is a nested list, in which case we want to apply <code>concat</code> and recur:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">(</span><span class="n">concat</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Sadly, if we try to compile these definitions, GHC will reject our <code>Flatten [a]</code> instance:</p><pre><code>error: + • Couldn't match type ‘a’ with ‘ElementOf [a]’ + ‘a’ is a rigid type variable bound by + the instance declaration + Expected type: [ElementOf [a]] + Actual type: [a] + • In the expression: x + In an equation for ‘flatten’: flatten x = x + In the instance declaration for ‘Flatten [a]’ + | + | flatten x = x + | ^ +</code></pre><p>At first blush, this error looks very confusing. Why doesn’t GHC think <code>a</code> and <code>ElementOf [a]</code> are the same type? Well, consider what would happen if we picked a type like <code>[Int]</code> for <code>a</code>. Then <code>[a]</code> would be <code>[[Int]]</code>, a nested list, so the first case of <code>ElementOf</code> would apply. Therefore, GHC refuses to pick the second equation of <code>ElementOf</code> so hastily.</p><p>In this particular case, we might think that’s rather silly. After all, if <code>a</code> were <code>[Int]</code>, then GHC wouldn’t have picked the <code>Flatten [a]</code> instance to begin with, it would pick the more specific <code>Flatten [[a]]</code> instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.</p><p>Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our <code>Flatten [a]</code> instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>This is a <em>type equality constraint</em>. Type equality constraints are written with the syntax <code>a ~ b</code>, and they state that <code>a</code> must be the same type as <code>b</code>. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that <code>ElementOf [a]</code> must always be <code>a</code>, which allows the instance to typecheck.</p><p>Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must <em>eventually</em> be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the <code>Flatten [a]</code> instance is selected, GHC will know that <code>a</code> is <em>not</em> a list type, and it will be able to reduce <code>ElementOf [a]</code> to <code>a</code> without difficulty. Indeed, we can see this for ourselves by using <code>flatten</code> in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span></code></pre><p>It works! But why do we need the type annotation on <code>1</code>? If we leave it out, we get a rather hairy type error:</p><pre><code>error: + • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ + Expected type: [ElementOf [a]] + Actual type: [ElementOf [a0]] + NB: ‘ElementOf’ is a non-injective type family + The type variable ‘a0’ is ambiguous +</code></pre><p>The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a <code>Num [a]</code> instance, in which case <code>1</code> could actually have a list type, and either case of <code>ElementOf</code> could match depending on the choice of <code>Num</code> instance. Of course, no such <code>Num</code> instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.</p><p>This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call <code>flatten</code> on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.</p><p>That wrinkle aside, <code>flatten</code> is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.</p><h4><a name="typeclasses-as-compile-time-code-generation"></a>Typeclasses as compile-time code generation</h4><p>Presented with the above definition of <code>Flatten</code>, it might not be immediately obvious how to think about <code>Flatten</code> as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, <code>Eq</code> or <code>Show</code>) than the <code>TypeOf</code> and <code>ReifyNat</code> classes we defined above.</p><p>One useful way to shift our perspective is to consider equivalent <code>Flatten</code> instances written using point-free style:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">id</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">concat</span></code></pre><p>These definitions of <code>flatten</code> no longer (syntactically) depend on term-level arguments, just like our definitions of <code>typeOf</code> and <code>reifyNat</code> didn’t accept any term-level arguments above. This allows us to consider what <code>flatten</code> might “expand to” given a type argument alone:</p><ul><li><p><code>flatten @[Int]</code> is just <code>id</code>, since the <code>Flatten [a]</code> instance is selected.</p></li><li><p><code>flatten @[[Int]]</code> is <code>flatten @[Int] . concat</code>, since the <code>Flatten [[a]]</code> instance is selected. That then becomes <code>id . concat</code>, which can be further simplified to just <code>concat</code>.</p></li><li><p><code>flatten @[[[Int]]]</code> is <code>flatten @[[Int]] . concat</code>, which simplifies to <code>concat . concat</code> by the same reasoning above.</p></li><li><p><code>flatten @[[[[Int]]]]</code> is then <code>concat . concat . concat</code>, and so on.</p></li></ul><p>This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of <code>flatten</code> takes a type as an argument and produces some number of composed <code>concat</code>s as a result. From this perspective, <code>Flatten</code> is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.</p><p>This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name <em>metaprogramming</em>. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.</p><h2><a name="part-2-generic-programming"></a>Part 2: Generic programming</h2><p>Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.</p><p>In the previous section, we discussed how to use TMP to write a generic <code>flatten</code> operation. In this section, we’ll aim a bit higher: totally generic functions that operate on <em>arbitrary</em> datatypes.</p><h3><a name="open-type-families-and-associated-types"></a>Open type families and associated types</h3><p>Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, <em>open type families</em>. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using <code>type instance</code> declarations. For example, we could define our <code>Sum</code> family from above like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>In the case of <code>Sum</code>, this would not be very useful, and indeed, <code>Sum</code> is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.</p><p>This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a <code>Key</code> open type family that relates types to the types used to index them:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span></code></pre><p>This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>In this case, anyone could define their own data structure, define instances of <code>Key</code> and <code>HasKey</code> for their data structure, and use <code>hasKey</code> to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>An open family declared inside a typeclass like this is called an <em>associated type</em>. It works exactly the same way as the separate definitions of <code>Key</code> and <code>HasKey</code>, it just uses a different syntax. Note that although the <code>family</code> and <code>instance</code> keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).</p><p>Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like <a href="https://hackage.haskell.org/package/mono-traversable"><code>mono-traversable</code></a> are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.</p><p>However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes <em>heavy</em> use of open type families: datatype-generic programming.</p><h3><a name="example-2-datatype-generic-programming"></a>Example 2: Datatype-generic programming</h3><p><em>Datatype-generic programming</em> refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include</p><ul><li><p>equality, comparison, and hashing,</p></li><li><p>recursive traversal of self-similar data structures, and</p></li><li><p>serialization and deserialization,</p></li></ul><p>among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on <em>any</em> datatype.</p><p>In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.</p><h4><a name="generic-datatype-representations"></a>Generic datatype representations</h4><p>At the heart of the <code>Generic</code> class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Authentication</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AuthBasic</span><span class="w"> </span><span class="kt">Username</span><span class="w"> </span><span class="kt">Password</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AuthSSH</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>then we have a type that is essentially equivalent to this one:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>If we know how to define a function on a nested tree built out of <code>Either</code>s and pairs, then we know how to define it on <em>any</em> such datatype! This is where TMP comes in: recall the way we viewed <code>Flatten</code> as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?</p><p>The answer to that question is <em>yes</em>. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, <code>numFields (AuthBasic "alyssa" "pass1234")</code> would return <code>2</code>, while <code>numFields (AuthSSH "&lt;key&gt;")</code> would return <code>1</code>. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.</p><p>We’ll start by using TMP to implement a “generic” version of <code>numFields</code> that operates on trees of <code>Either</code>s and pairs as described above:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="c1">-- base case: leaf value</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>Just like our <code>Flatten</code> class from earlier, <code>GNumFields</code> uses the type-level structure of its argument to choose what to do:</p><ul><li><p>If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.</p></li><li><p>If we find <code>Left</code> or <code>Right</code>, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.</p></li><li><p>In the case of any other value, we’re at a “leaf” in the tree of <code>Either</code>s and pairs, which corresponds to a single field, so we just return <code>1</code>.</p></li></ul><p>Now if we call <code>gnumFields (Left ("alyssa", "pass1234"))</code>, we’ll get <code>2</code>, and if we call <code>gnumFields (Right "&lt;key&gt;")</code>, we’ll get <code>1</code>. All that’s left to do is write a bit of code that converts our <code>Authentication</code> type to a tree of <code>Either</code>s and pairs:</p><pre><code class="pygments"><span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericizeAuthentication</span></code></pre><p>Now we get the results we want on our <code>Authentication</code> type using <code>numFieldsAuthentication</code>, but we’re not done yet, since it only works on <code>Authentication</code> values. Is there a way to define a generic <code>numFields</code> function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFields</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericize</span></code></pre><p>Now <code>numFields (AuthBasic "alyssa" "pass1234")</code> returns <code>2</code>, as desired, and it will <em>also</em> work with any datatype that provides a <code>Generic</code> instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:</p><ul><li><p>First, we define the <code>Generic</code> class, comprised of two parts:</p><ol><li><p>The <code>Rep a</code> associated type maps a type <code>a</code> onto its generic, sums-of-products representation, i.e. one built out of combinations of <code>Either</code> and pairs.</p></li><li><p>The <code>genericize</code> method converts an actual <em>value</em> of type <code>a</code> to the equivalent value using the sums-of-products representation.</p></li></ol></li><li><p>Next, we define a <code>Generic</code> instance for <code>Authentication</code>. <code>Rep Authentication</code> is the sums-of-products representation we described above, and <code>genericize</code> is likewise <code>genericizeAuthentication</code> from above.</p></li><li><p>Finally, we define <code>numFields</code> as a function with a <code>GNumFields (Rep a)</code> constraint. This is where all the magic happens:</p><ul><li><p>When we apply <code>numFields</code> to a datatype, <code>Rep</code> retrieves its generic, sums-of-products representation type.</p></li><li><p>The <code>GNumFields</code> class then uses various TMP techniques we’ve already described so far in this blog post to generate a <code>numFields</code> implementation on the fly from the structure of <code>Rep a</code>.</p></li><li><p>Finally, that generated <code>numFields</code> implementation is applied to the genericized term-level value, and the result is produced.</p></li></ul></li></ul><p>After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) <code>numFields</code> operation. Surely just defining <code>numFields</code> on each type directly would be far easier? Indeed, if we were just considering <code>numFields</code>, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined <code>numFields</code>, and all of them would automatically work on <code>Authentication</code> because they all leverage the same <code>Generic</code> instance!</p><p>This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation <em>once</em>, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.</p><h4><a name="improving-our-definition-of-generic"></a>Improving our definition of <code>Generic</code></h4><p>You may note that the definition of <code>Generic</code> provided above does not match the one in <code>GHC.Generic</code>. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a <code>GHC.Generics</code> tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.</p><h5><a name="distinguishing-leaves-from-the-spine"></a>Distinguishing leaves from the spine</h5><p>One problem with our version of <code>Generic</code> is that it provides no way to distinguish an <code>Either</code> or pair that should be considered a “leaf”, as in a type like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">A</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">B</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">)</span></code></pre><p>Given this type, <code>Rep Foo</code> should be <code>Either (Either Int String) (Char, Bool)</code>, and <code>numFields (Right ('a', True))</code> will erroneously return <code>2</code> rather than <code>1</code>. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">getLeaf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Now our <code>Generic</code> instances look like this:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">PublicKey</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">key</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">))</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">A</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">B</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Since the <code>Leaf</code> constructor now distinguishes a leaf, rather than the absence of an <code>Either</code> or <code>(,)</code> constructor, we’ll have to update our <code>GNumFields</code> instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.</p><h5><a name="handling-empty-constructors"></a>Handling empty constructors</h5><p>Suppose we have a type with nullary data constructors, like the standard <code>Bool</code> type:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>How do we write a <code>Generic</code> instance for <code>Bool</code>? Using just <code>Either</code>, <code>(,)</code>, and <code>Leaf</code>, we can’t, but if we are willing to add a case for <code>()</code>, we can use it to denote nullary constructors:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="nb">()</span></code></pre><p>In a similar vein, we could use <code>Void</code> to represent datatypes that don’t have any constructors at all.</p><h4><a name="continuing-from-here"></a>Continuing from here</h4><p>The full version of <code>Generic</code> has a variety of further improvements useful for generic programming, including:</p><ul><li><p>Support for converting from <code>Rep a</code> to <code>a</code>.</p></li><li><p>Special indication of self-recursive datatypes, making generic tree traversals possible.</p></li><li><p>Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.</p></li><li><p>Fully automatic generation of <code>Generic</code> instances via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric">the <code>DeriveGeneric</code> extension</a>, which reduces the per-type boilerplate to essentially nothing.</p></li></ul><p>The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.</p><h2><a name="part-3-dependent-typing"></a>Part 3: Dependent typing</h2><p>It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.</p><h3><a name="datatype-promotion"></a>Datatype promotion</h3><p>In part 1, we used uninhabited datatypes like <code>Z</code> and <code>S a</code> to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, <code>True</code> and <code>False</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">True</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">False</span></code></pre><p>Now we could define type families to provide operations on these types, such as <code>Not</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>However, this has some frustrating downsides:</p><ul><li><p>First, it’s simply inconvenient that we have to define these new <code>True</code> and <code>False</code> “dummy” types, which are completely distinct from the <code>Bool</code> type provided by the prelude.</p></li><li><p>More significantly, it means <code>Not</code> has a very unhelpful kind:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span></code></pre><p>Even though <code>Not</code> is only <em>supposed</em> to be applied to <code>True</code> or <code>False</code>, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like <code>Not Char</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span> +<span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span></code></pre><p>Rather than getting an error, GHC simply spits <code>Not Char</code> back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.</p></li></ul><p>One way to think about <code>Not</code> is that it is largely <em>dynamically kinded</em> in the same way some languages are dynamically typed. That isn’t entirely true, as we technically <em>will</em> get a kind error if we try to apply <code>Not</code> to a type constructor rather than a type, such as <code>Maybe</code>:</p><pre><code>ghci&gt; :kind! Not Maybe + +&lt;interactive&gt;:1:5: error: + • Expecting one more argument to ‘Maybe’ + Expected a type, but ‘Maybe’ has kind ‘* -&gt; *’ +</code></pre><p>…but <code>*</code> is still a very big kind, much bigger than we would like to permit for <code>Not</code>.</p><p>To help with both these problems, GHC provides <em>datatype promotion</em> via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html">the <code>DataKinds</code> language extension</a>. The idea is that for each normal, non-GADT type definition like</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>then in addition to the normal type constructor and value constructors, GHC also defines several <em>promoted</em> constructors:</p><ul><li><p><code>Bool</code> is allowed as both a type and a kind.</p></li><li><p><code>'True</code> and <code>'False</code> are defined as new types of kind <code>Bool</code>.</p></li></ul><p>We can see this in action if we remove our <code>data True</code> and <code>data False</code> declarations and adjust our definition of <code>Not</code> to use promoted constructors:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DataKinds #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;True</span></code></pre><p>Now the inferred kind of <code>Not</code> is no longer <code>* -&gt; *</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>Consequently, we will now get a kind error if we attempt to apply <code>Not</code> to anything other than <code>'True</code> or <code>'False</code>:</p><pre><code>ghci&gt; :kind! Not Char + +&lt;interactive&gt;:1:5: error: + • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ +</code></pre><p>This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">&#39;Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">&#39;S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>Note that we need to add an explicit kind signature on the definition of the <code>ReifyNat</code> typeclass, since otherwise GHC will assume <code>a</code> has kind <code>*</code>, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that <code>Z</code> and <code>S</code> are related, this prevents someone from coming along and defining a nonsensical instance like <code>ReifyNat Char</code>, which previously would have been allowed but will now be rejected with a kind error.</p><p>Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.</p><h3><a name="gadts-and-proof-terms"></a>GADTs and proof terms</h3><p>So far in this blog post, we have discussed several different function-like things:</p><ul><li><p>Ordinary Haskell functions are functions from terms to terms.</p></li><li><p>Type families are functions from types to types.</p></li><li><p>Typeclasses are functions from types to terms.</p></li></ul><p>A curious reader may wonder about the existence of a fourth class of function:</p><ul><li><p><em>???</em> are functions from terms to types.</p></li></ul><p>To reason about what could go in the <em>???</em> above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?</p><p>In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.</p><p>GADTs<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup> are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html">described in detail in the GHC User’s Guide</a>, but the key idea for our purposes is that <em>pattern-matching on a GADT constructor can refine type information</em>. Here’s a simple, silly example:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Bool</span> +<span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Int</span> + +<span class="nf">doSomething</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">x</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span></code></pre><p>Here, <code>WhatIsIt</code> is a datatype with two nullary constructors, <code>ABool</code> and <code>AnInt</code>, similar to a normal, non-GADT datatype like this one:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AnInt</span></code></pre><p>What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, <code>ABool</code> and <code>AnInt</code> would both have the type <code>forall a. WhatIsIt a</code>, but in the GADT definition, we explicitly fix <code>a</code> to <code>Bool</code> in the type of <code>ABool</code> and to <code>Int</code> in the type of <code>AnInt</code>.</p><p>This simple feature allows us to do very interesting things. The <code>doSomething</code> function is polymorphic in <code>a</code>, but on the right-hand side of the first equation, <code>x</code> has type <code>Bool</code>, while on the right-hand side of the second equation, <code>x</code> has type <code>Int</code>. This is because the <code>WhatIsIt a</code> argument effectively constrains the type of <code>a</code>, as we can see by experimenting with <code>doSomething</code> in GHCi:</p><pre><code>ghci&gt; doSomething ABool True +False +ghci&gt; doSomething AnInt 10 +11 +ghci&gt; doSomething AnInt True + +error: + • Couldn't match expected type ‘Int’ with actual type ‘Bool’ + • In the second argument of ‘doSomething’, namely ‘True’ + In the expression: doSomething AnInt True + In an equation for ‘it’: it = doSomething AnInt True +</code></pre><p>One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The <code>ABool</code> constructor is a proof of <code>a ~ Bool</code>, while the <code>AnInt</code> constructor is a proof of <code>a ~ Int</code>. When you construct <code>ABool</code> or <code>AnInt</code>, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.</p><p>GADTs can be much more sophisticated than our simple <code>WhatIsIt</code> type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:</p><pre><code class="pygments"><span class="kr">infixr</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span> + +<span class="kr">data</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">HCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This type is a <em>heterogenous list</em>, a list that can contain elements of different types:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">t</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Num</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[Bool, [Char]</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>An <code>HList</code> is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total <code>head</code> function on <code>HList</code>s like this:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Remarkably, GHC does not complain that this definition of <code>head</code> is non-exhaustive. Since we specified that the argument must be of type <code>HList (a ': as)</code> in the type signature for <code>head</code>, GHC knows that the argument <em>cannot</em> be <code>HNil</code> (which would have the type <code>HList '[]</code>), so it doesn’t ask us to handle that case.</p><p>These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.</p><h4><a name="proofs-that-work-together"></a>Proofs that work together</h4><p>This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an <code>HList</code> of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with <code>head</code>, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.</p><p>However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a <em>proof term</em> that contains no values, it just encapsulates type equalities on a type-level list:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a]</span> +<span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b]</span> +<span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b, c]</span></code></pre><p>We call it a proof term because a value of type <code>OneToThree a b c as</code> constitutes a <em>proof</em> that <code>as</code> has exactly 1, 2, or 3 elements. Using <code>OneToThree</code>, we can write a function that accepts an <code>HList</code> accompanied by a proof term:</p><pre><code class="pygments"><span class="nf">sumUpToThree</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">z</span></code></pre><p>As with <code>head</code>, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:</p><ul><li><p>When we match on the <code>OneToThree</code> proof term, information flows from the term level to the type level, refining the type of <code>as</code> in that branch.</p></li><li><p>The refined type of <code>as</code> then flows back down to the term level, restricting the shape the <code>HList</code> can take and refinine the set of patterns we have to match.</p></li></ul><p>Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an <code>HList</code> has an even number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This is a proof which itself has inductive structure: <code>EvenCons</code> takes a proof that <code>as</code> has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an <code>HList</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span> + +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Once again, this definition is completely exhaustive, and we can show that it works in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="kt">EvenNil</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.</p><h4><a name="proof-inference"></a>Proof inference</h4><p>While our definition of <code>pairUp</code> is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the <code>Even</code> proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.</p><p>Remember that typeclasses are functions from types to terms. As its happens, a value of type <code>Even as</code> can be mechanically produced from the structure of the type <code>as</code>. This suggests that we could use TMP to automatically generate <code>Even</code> proofs, and indeed, we can. In fact, it’s not at all complicated:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>We can now adjust our <code>pairUp</code> function to use <code>IsEven</code> instead of an explicit <code>Even</code> argument:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>This is essentially identical to its old definition, but by acquiring the proof via <code>IsEven</code> rather than passing it explicitly, we can call <code>pairUp</code> without having to construct a proof manually:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This is rather remarkable. Using TMP, we are able to get GHC to <em>automatically construct a proof that a list is even</em>, with no programmer guidance beyond writing the <code>IsEven</code> typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: <code>IsEven</code> is a function that accepts a type-level list and generates an <code>Even</code> proof term.</p><p>From this perspective, <strong>typeclasses are a way of specifying a proof search algorithm</strong> to the compiler. In the case of <code>IsEven</code>, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.</p><h3><a name="aside-gadts-versus-type-families"></a>Aside: GADTs versus type families</h3><p>Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.</p><p>Consider again the type of the <code>pairUp</code> function above (without the typeclass for simplicity):</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>We used both a GADT, <code>Even</code>, and a type family, <code>PairUp</code>. But we could have, in theory, used <em>only</em> a GADT and eliminated the type family altogether. Consider this variation on the <code>Even</code> proof term:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="p">((</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span></code></pre><p>This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of <code>as</code> as an “input” parameter and <code>bs</code> as an “output” parameter. The idea is that any <code>EvenPairs</code> proof relates both an even-length list type and its paired up equivalent:</p><ul><li><p><code>EvenNil</code> has type <code>EvenPairs '[] '[]</code>,</p></li><li><p><code>EvenCons EvenNil</code> has type <code>EvenPairs '[a, b] '[(a, b)]</code>,</p></li><li><p><code>EvenCons (EvenCons EvenNil)</code> has type <code>EvenPairs '[a, b, c, d] '[(a, b), (c, d)]</code>,</p></li><li><p>…and so on.</p></li></ul><p>This allows us to reformulate our <code>pairUp</code> type signature this way:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">bs</span></code></pre><p>The definition is otherwise unchanged. The <code>PairUp</code> type family is completely gone, because now <code>EvenPairs</code> itself defines the relation. In this way, GADTs can be used like type-level functions!</p><p>The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Kind</span><span class="w"> </span><span class="p">(</span><span class="kt">Constraint</span><span class="p">)</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span></code></pre><p>The idea here is that <code>IsEvenTF as</code> produces a constraint can only be satisfied if <code>as</code> has an even number of elements, since that’s the only way it will eventually reduce to <code>()</code>, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting <code>IsEvenTF as =&gt;</code> in a type signature successfully restricts <code>as</code> to be an even-length list, but it doesn’t allow us to write <code>pairUp</code>. To see why, we can try the following definition:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Unlike the version using the GADT, this version of <code>pairUp</code> is not considered exhaustive:</p><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil +</code></pre><p>This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by <code>IsEvenTF</code> provides no term-level evidence about the shape of <code>as</code>, so we can’t branch on it the way we can branch on the <code>Even</code> GADT.<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> (In a sense, <code>IsEvenTF</code> is doing <a href="/blog/2019/11/05/parse-don-t-validate/">validation, not parsing</a>.)</p><p>For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.</p><h3><a name="guiding-type-inference"></a>Guiding type inference</h3><p>So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.</p><p>However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.</p><p>To illustrate what that can look like, suppose we want to use TMP to generate an <code>HList</code> full of <code>()</code> values of an arbitrary length:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Testing in GHCi, we can see it behaves as desired:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[(), (), ()]</span> +<span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>Now suppose we write a function that accepts a list containing exactly one element and returns it:</p><pre><code class="pygments"><span class="nf">unsingleton</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[a]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unsingleton</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Naturally, we would expect these to compose without a hitch. If we write <code>unsingleton unitList</code>, our TMP should generate a list of length 1, and we should get back <code>()</code>. However, it may surprise you to learn that <em>isn’t</em>, in fact, what happens:<sup><a href="#footnote-6" id="footnote-ref-6-1">6</a></sup></p><pre><code>ghci&gt; unsingleton unitList + +error: + • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ + prevents the constraint ‘(UnitList '[a0])’ from being solved. + Probable fix: use a type annotation to specify what ‘a0’ should be. + These potential instances exist: + instance UnitList as =&gt; UnitList (() : as) +</code></pre><p>What went wrong? The type error says that <code>a0</code> is ambiguous, but it only lists a single matching <code>UnitList</code> instance—the one we want—so how can it be ambiguous which one to select?</p><p>The problem stems from the way we defined <code>UnitList</code>. When we wrote the instance</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span></code></pre><p>we said the first element of the type-level list must be <code>()</code>, so there’s nothing stopping someone from coming along and defining another instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="kt">Int</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>In that case, GHC would have no way to know which instance to pick. Nothing in the type of <code>unsingleton</code> forces the element in the list to have type <code>()</code>, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.</p><p>Of course, this isn’t what we want. The <code>UnitList</code> class is supposed to <em>always</em> return a list of <code>()</code> values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Here we’ve changed the instance so that it has the shape <code>UnitList (a ': as)</code>, with a type variable in place of the <code>()</code>, but we also added an equality constraint that forces <code>a</code> to be <code>()</code>. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unsingleton</span><span class="w"> </span><span class="n">unitList</span> +<span class="nb">()</span></code></pre><p>To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="o">&lt;</span><span class="n">constraints</span><span class="o">&gt;</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">C</span><span class="w"> </span><span class="o">&lt;</span><span class="n">types</span><span class="o">&gt;</span></code></pre><p>The part to the left of the <code>=&gt;</code> is known as the <em>instance context</em>, while the part to the right is known as the <em>instance head</em>. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, <strong>only the instance head matters, and the instance context is completely ignored</strong>. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.</p><p>This explains why our two <code>UnitList</code> instances behave differently:</p><ul><li><p>Given the instance head <code>UnitList (() ': as)</code>, GHC won’t select the instance unless it knows the first element of the list is <code>()</code>.</p></li><li><p>But given the instance head <code>UnitList (a ': as)</code>, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.</p></li></ul><p>After the <code>UnitList (a ': as)</code> instance is selected, GHC attempts to solve the constraints in the instance context, including the <code>a ~ ()</code> constraint. This <em>forces</em> <code>a</code> to be <code>()</code>, resolving the ambiguity and allowing type inference to proceed.</p><p>This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:</p><ul><li><p>If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.</p></li><li><p>But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell <em>you</em> what type this is,” effectively giving you a role in type inference itself.</p></li></ul><p>From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.</p><p>Given all of the above, consider again the definition of <code>IsEven</code> from earlier:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an <code>IsEven (a ': b0)</code> constraint, where <code>b0</code> is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an <code>IsEven '[a]</code> instance in the future.</p><p>To fix this, we can apply the same trick we used for <code>UnitList</code>, just in a slightly different way:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Again, the idea is to move the type information we <em>learn</em> from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can <strong>dramatically</strong> improve type inference in programs that make heavy use of TMP.</p><h3><a name="example-3-subtyping-constraints"></a>Example 3: Subtyping constraints</h3><p>At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at <a href="https://hasura.io/">Hasura</a>, I had the opportunity to design an internal parser combinator library that captures aspects of the <a href="https://graphql.org/">GraphQL</a> type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.</p><p>Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our <code>GQLKind</code> datatype has three cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLKind</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Both</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Input</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Output</span></code></pre><p>We use <code>DataKind</code>-promoted versions of this <code>GQLKind</code> type as a parameter to a <code>GQLType</code> GADT:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">TScalar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Both</span> +<span class="w"> </span><span class="kt">TInputObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">InputObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Input</span> +<span class="w"> </span><span class="kt">TIObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Output</span> +<span class="w"> </span><span class="c1">-- ...and so on...</span></code></pre><p>This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type <code>GQLType 'Input</code>, we can’t pass a <code>GQLType 'Both</code>, even though we really ought to be able to.</p><p>To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span></code></pre><p>The first case, <code>KRefl</code>, states that every kind is trivially a subkind of itself. The second case, <code>KBoth</code>, states that <code>Both</code> is a subkind of any kind at all. (This is a particularly literal example of <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">using a type to define axioms</a>.) The next step is to use TMP to implement proof inference:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KBoth</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span></code></pre><p>These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that <code>k</code> is a superkind of <code>'Input</code> or <code>'Output</code>, type inference will force them to be equal.</p><p>Using <code>IsSubKind</code>, we can easily resolve the problem described above. Rather than write a function with a type like this:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>…we simply use an <code>IsSubKind</code> constraint, instead:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now both <code>'Input</code> and <code>'Both</code> kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. <em>Consuming</em> the <code>SubKind</code> proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SelectionSet</span></code></pre><p>This type family is used to determine what a <code>GQLParser k a</code> actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>.</p><p>Fortunately, that is very easy to do using <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html">the <code>(:~:)</code> type from <code>Data.Type.Equality</code> in <code>base</code></a> to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">a</span></code></pre><p>Just as with any other GADT, <code>(:~:)</code> can be used to pack up type equalities and unpack them later; <code>a :~: b</code> just happens to be the GADT that corresponds precisely to the equality <code>a ~ b</code>. Using <code>(:~:)</code>, we can write a reusable proof that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>:</p><pre><code class="pygments"><span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="o">@</span><span class="kt">&#39;Input</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span></code></pre><p>This function is a very simple proof by cases, where <code>Refl</code> can be read as “Q.E.D.”:</p><ul><li><p>In the first case, matching on <code>KRefl</code> refines <code>k</code> to <code>'Input</code>, and <code>ParserInput 'Input</code> is <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li><li><p>Likewise, in the second case, matching on <code>KBoth</code> refines <code>k</code> to <code>'Both</code>, and <code>ParserInput 'Both</code> is also <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li></ul><p>This <code>inputParserInput</code> helper allows functions like <code>nullable</code>, which internally need <code>ParserInput k ~ InputValue</code>, to take the form</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nullable</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">inputParserInput</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ...implementation goes here... -}</span></code></pre><p>Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without <code>IsSubKind</code> doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!</p><h2><a name="wrapping-up-and-closing-thoughts"></a>Wrapping up and closing thoughts</h2><p>So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:</p><ul><li><p>Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.</p></li><li><p>Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.</p></li><li><p>Though I’ve called the technique “<em>typeclass</em> metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.</p></li><li><p>Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.</p></li></ul><p>The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.</p><p>Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.</p><p>It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:</p><ul><li><p>As mentioned earlier in this blog post, <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">the <code>GHC.Generics</code> module documentation</a> is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.</p></li><li><p>I have long believed that <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/">the GHC User’s Guide</a> is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.</p></li><li><p>Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the <a href="https://hackage.haskell.org/package/singletons">singletons</a> library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, <a href="https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf">the accompanying paper</a> is definitely worth a read if you’d like to go down that route.)</p></li></ul><p>Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.</p><ol class="footnotes"><li id="footnote-1"><p>Not to be confused with C++’s <a href="https://en.wikipedia.org/wiki/Template_metaprogramming"><em>template</em> metaprogramming</a>, though there are significant similarities between the two techniques. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>There have been proposals to introduce ordered instances, known in the literature as <a href="https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf"><em>instance chains</em></a>, but as of this writing, GHC does not implement them. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Note that this also preserves an important property of the Haskell type system, parametricity. A function like <code>id :: a -&gt; a</code> shouldn’t be allowed to do different things depending on which type is chosen for <code>a</code>, which our first version of <code>guardUnit</code> tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Short for <em>generalized algebraic datatypes</em>, which is a rather unhelpful name for actually understanding what they are or what they’re for. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for <code>IsEvenTF</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">exists</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">as&#39;</span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">)</span></code></pre><p>The type refinement provided by matching on <code>HCons</code> would be enough for the second case of <code>IsEvenTF</code> to be selected, which would provide an equality proof that <code>as</code> has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. <a href="#footnote-ref-5-1">↩</a></p></li><li id="footnote-6"><p>Actually, I’ve cheated a little bit here, because <code>unsingleton unitList</code> really does typecheck in GHCi under normal circumstances. That’s because <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules">the <code>ExtendedDefaultRules</code> extension</a> is enabled in GHCi by default, which defaults ambiguous type variables to <code>()</code>, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned <code>ExtendedDefaultRules</code> off to illustrate the problem. <a href="#footnote-ref-6-1">↩</a></p></li></ol></article>Names are not type safetyhttps://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/01 Nov 2020<article><p>Haskell programmers spend a lot of time talking about <em>type safety</em>. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published <a href="/blog/2019/11/05/parse-don-t-validate/">Parse, Don’t Validate</a> as an initial stab towards bridging that gap.</p><p>The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s <code>newtype</code> construct. The idea is simple enough—the <code>newtype</code> keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this <em>sounds</em> like a simple and straightforward path to type safety. For example, one might consider using a <code>newtype</code> declaration to define a type for an email address:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This technique can provide <em>some</em> value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct <em>kind</em> of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.</p><p>And names are not type safety.</p><h2><a name="intrinsic-and-extrinsic-safety"></a>Intrinsic and extrinsic safety</h2><p>To illustrate the difference between constructive data modeling (discussed at length in my <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">previous blog post</a>) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">One</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Two</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Three</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Four</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Five</span></code></pre><p>We could then write some functions to convert between <code>Int</code> and our <code>OneToFive</code> type:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">One</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Two</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Three</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Four</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Five</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">3</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">5</span></code></pre><p>This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="kt">Int</span></code></pre><p>Just as before, we can provide <code>toOneToFive</code> and <code>fromOneToFive</code> functions, with identical types:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">otherwise</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="p">(</span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">n</span></code></pre><p>If we put these declarations in their own module and choose not to export the <code>OneToFive</code> constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.</p><p>To see why, suppose we write a function that consumes a <code>OneToFive</code> value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"first"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"second"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"third"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fifth"</span></code></pre><p>The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an <code>Int</code>—after all, it <em>is</em> an <code>Int</code>. An <code>Int</code> can of course contain many other values besides <code>1</code> through <code>5</code>, so we are forced to add an error case to satisfy the exhaustiveness checker:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromOneToFive</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"first"</span> +<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"second"</span> +<span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"third"</span> +<span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fifth"</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: bad OneToFive value"</span></code></pre><p>In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:</p><ul><li><p>The constructive datatype captures its invariants in such a way that they are <em>accessible</em> to downstream consumers. This frees our <code>ordinal</code> function from worrying about handling illegal values, as they have been made unutterable.</p></li><li><p>The newtype wrapper provides a smart constructor that <em>validates</em> the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting <code>Int</code>s.</p></li></ul><p>Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of <code>error</code> has punched a hole right through our type system. If we were to add another constructor to our <code>OneToFive</code> datatype,<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> the version of <code>ordinal</code> that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.</p><p>All of this is a consequence of the fact that the constructive modeling is <em>intrinsically</em> type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent <code>6</code> using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an <code>Int</code>; its meaning is specified extrinsically via the <code>toOneToFive</code> smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.</p><h3><a name="revisiting-non-empty-lists"></a>Revisiting non-empty lists</h3><p>Our <code>OneToFive</code> datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the <code>NonEmpty</code> datatype I’ve repeatedly highlighted in recent blog posts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>It may be illustrative to imagine a version of <code>NonEmpty</code> represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Foldable</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Just as with <code>OneToFive</code>, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for <code>NonEmpty</code> was the ability to write a safe version of <code>head</code>, but the newtype version requires another assertion:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">x</span> +<span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines <code>NonEmpty</code>, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.</p><h2><a name="newtypes-as-tokens"></a>Newtypes as tokens</h2><p>If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes <em>can</em> provide a sort of safety, just a weaker one.</p><p>The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a <em>trust boundary</em> where internal invariants are enforced by restricting clients to a safe API.</p><p>We can use the <code>NonEmpty</code> example from above to illustrate how this works. We refrain from exporting the <code>NonEmpty</code> constructor, and we provide <code>head</code> and <code>tail</code> operations that we trust to never actually fail:</p><pre><code class="pygments"><span class="kr">module</span><span class="w"> </span><span class="nn">Data.List.NonEmpty.Newtype</span> +<span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">NonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">cons</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">nonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">head</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">tail</span> +<span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">cons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span> +<span class="nf">cons</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span> + +<span class="nf">tail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>Since the only way to construct or consume <code>NonEmpty</code> values is to use the functions in <code>Data.List.NonEmpty.Newtype</code>’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like <em>tokens</em>: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case <code>head</code> and <code>tail</code>, to obtain the values contained within.</p><p>This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid <code>NonEmpty []</code> value. For this reason, the newtype approach to type safety does not on its own constitute a <em>proof</em> that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a <code>Generic</code> instance for <code>NonEmpty</code>:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DeriveGeneric #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">GHC.Generics</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span></code></pre><p>However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">GHC</span><span class="o">.</span><span class="kt">Generics</span><span class="o">.</span><span class="n">to</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">K1</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>This is a particularly extreme example, since derived <code>Generic</code> instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived <code>Read</code> instance:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">read</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="s">"NonEmpty []"</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:</p><ul><li><p>All invariants must be made clear to maintainers of the trusted module. For simple types, such as <code>NonEmpty</code>, the invariant is self-evident, but for more sophisticated types, comments are not optional.</p></li><li><p>Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.</p></li><li><p>Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.</p></li><li><p>Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.</p></li></ul><p>In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.</p><p>In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.</p><h2><a name="other-newtype-use-abuse-and-misuse"></a>Other newtype use, abuse, and misuse</h2><p>The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:</p><ul><li><p>Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the <code>Sum</code> and <code>Product</code> newtypes from <code>Data.Monoid</code> provide useful <code>Monoid</code> instances for numeric types.</p></li><li><p>In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The <code>Flip</code> newtype from <code>Data.Bifunctor.Flip</code> is a simple example, flipping the arguments of a <code>Bifunctor</code> so the <code>Functor</code> instance may operate on the other side:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runFlip</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.</p></li><li><p>More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a <code>ByteString</code> containing a secret key may be wrapped in a newtype (with a <code>Show</code> instance omitted) to discourage code from accidentally logging or otherwise exposing it.</p></li></ul><p>All of these applications are good ones, but they have little to do with <em>type safety.</em> The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually <em>prevents</em> misuse; any part of the program may inspect the value at any time.</p><p>Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">unArgumentName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSONKey</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSONKey</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">Hashable</span><span class="p">,</span><span class="w"> </span><span class="kt">ToTxt</span><span class="p">,</span><span class="w"> </span><span class="kt">Lift</span><span class="p">,</span><span class="w"> </span><span class="kt">Generic</span><span class="p">,</span><span class="w"> </span><span class="kt">NFData</span><span class="p">,</span><span class="w"> </span><span class="kt">Cacheable</span><span class="w"> </span><span class="p">)</span></code></pre><p>This newtype is useless noise. Functionally, it is completely interchangeable with its underlying <code>Name</code> type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an <code>ArgumentName</code>, since the enclosing field name already makes its role clear.</p><p>Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:</p><ul><li><p>Primarily, types distinguish <em>functional</em> differences between values. A value of type <code>NonEmpty a</code> is <em>functionally</em> distinct from a value of type <code>[a]</code>, since it is fundamentally structurally different and permits additional operations. In this sense, types are <em>structural</em>; they describe what values <em>are</em> in the internal world of the programming language.</p></li><li><p>Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate <code>Distance</code> and <code>Duration</code> types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.</p></li></ul><p>Note that both these uses are <em>pragmatic</em>; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system <em>is</em> a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like <code>ArgumentName</code>.</p><p>If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use <code>Name</code>, but in situations where the different label adds genuine clarity, one can always use a type alias:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span></code></pre><p>Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.</p><h2><a name="final-thoughts-and-related-reading"></a>Final thoughts and related reading</h2><p>I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.</p><p>Newtypes are one particular mechanism of defining <em>wrapper types</em>, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.</p><p>The catalyst that got me to finally sit down and write this was the recently-published <a href="https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/">Tagged is not a Newtype</a>. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, <code>Tagged</code> <em>is</em> a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.</p><p>Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.</p><p>And a name is not type safety.</p><ol class="footnotes"><li id="footnote-1"><p>Admittedly rather unlikely given its name, but bear with me through the contrived example. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Types as axioms, or: playing god with static typeshttps://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/13 Aug 2020<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more openhttps://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/19 Jan 2020<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Parse, don’t validatehttps://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/05 Nov 2019<article><p>Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.</p><p>However, about a month ago, <a href="https://twitter.com/lexi_lambda/status/1182242561655746560">I was reflecting on Twitter</a> about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:</p><div style="text-align: center; font-size: larger"><strong>Parse, don’t validate.</strong></div><h2><a name="the-essence-of-type-driven-design"></a>The essence of type-driven design</h2><p>Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.</p><h3><a name="the-realm-of-possibility"></a>The realm of possibility</h3><p>One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:</p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Void</span></code></pre><p>Is it possible to implement <code>foo</code>? Trivially, the answer is <em>no</em>, as <code>Void</code> is a type that contains no values, so it’s impossible for <em>any</em> function to produce a value of type <code>Void</code>.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘head’: Patterns not matched: [] +</code></pre><p>This message is helpfully pointing out that our function is <em>partial</em>, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is <code>[]</code>, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.</p><h3><a name="turning-partial-functions-total"></a>Turning partial functions total</h3><p>To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the <code>head</code> function, and we’ll start with the simplest one.</p><h4><a name="managing-expectations"></a>Managing expectations</h4><p>As established, <code>head</code> is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the <code>Maybe</code> type:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span></code></pre><p>This buys us the freedom we need to implement <code>head</code>—it allows us to return <code>Nothing</code> when we discover we can’t produce a value of type <code>a</code> after all:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>Problem solved, right? For the moment, yes… but this solution has a hidden cost.</p><p>Returning <code>Maybe</code> is undoubtably convenient when we’re <em>implementing</em> <code>head</code>. However, it becomes significantly less convenient when we want to actually use it! Since <code>head</code> always has the potential to return <code>Nothing</code>, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">configDirsList</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">configDirsList</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">cacheDir</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="n">cacheDir</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"should never happen; already checked configDirs is non-empty"</span></code></pre><p>When <code>getConfigurationDirectories</code> retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use <code>head</code> in <code>main</code> to get the first element of the list, the <code>Maybe FilePath</code> result still requires us to handle a <code>Nothing</code> case that we know will never happen! This is terribly bad for several reasons:</p><ol><li><p>First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?</p></li><li><p>Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.</p></li><li><p>Finally, and worst of all, this code is a bug waiting to happen! What if <code>getConfigurationDirectories</code> were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update <code>main</code>, and suddenly the “impossible” error becomes not only possible, but probable.</p></li></ol><p>The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically <em>prove</em> the <code>Nothing</code> case impossible, then a modification to <code>getConfigurationDirectories</code> that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.</p><h4><a name="paying-it-forward"></a>Paying it forward</h4><p>Clearly, our modified version of <code>head</code> leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, <code>head</code> should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?</p><p>Let’s look at the original (partial) type signature for <code>head</code> again:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, <code>[a]</code>). Instead of weakening the return type, we can <em>strengthen</em> the argument type, eliminating the possibility of <code>head</code> ever being called on an empty list in the first place.</p><p>To do this, we need a type that represents non-empty lists. Fortunately, the existing <code>NonEmpty</code> type from <code>Data.List.NonEmpty</code> is exactly that. It has the following definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>Note that <code>NonEmpty a</code> is really just a tuple of an <code>a</code> and an ordinary, possibly-empty <code>[a]</code>. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the <code>[a]</code> component is <code>[]</code>, the <code>a</code> component must always be present. This makes <code>head</code> completely trivial to implement:<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Unlike before, GHC accepts this definition without complaint—this definition is <em>total</em>, not partial. We can update our program to use the new implementation:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">FilePath</span><span class="p">)</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">nonEmpty</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="p">)</span></code></pre><p>Note that the redundant check in <code>main</code> is now completely gone! Instead, we perform the check exactly once, in <code>getConfigurationDirectories</code>. It constructs a <code>NonEmpty a</code> from a <code>[a]</code> using the <code>nonEmpty</code> function from <code>Data.List.NonEmpty</code>, which has the following type:</p><pre><code class="pygments"><span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>The <code>Maybe</code> is still there, but this time, we handle the <code>Nothing</code> case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a <code>NonEmpty FilePath</code> value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type <code>NonEmpty a</code> as being like a value of type <code>[a]</code>, plus a <em>proof</em> that the list is non-empty.</p><p>By strengthening the type of the argument to <code>head</code> instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:</p><ul><li><p>The code has no redundant checks, so there can’t be any performance overhead.</p></li><li><p>Furthermore, if <code>getConfigurationDirectories</code> changes to stop checking that the list is non-empty, its return type must change, too. Consequently, <code>main</code> will fail to typecheck, alerting us to the problem before we even run the program!</p></li></ul><p>What’s more, it’s trivial to recover the old behavior of <code>head</code> from the new one by composing <code>head</code> with <code>nonEmpty</code>:</p><pre><code class="pygments"><span class="nf">head&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fmap</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">nonEmpty</span></code></pre><p>Note that the inverse is <em>not</em> true: there is no way to obtain the new version of <code>head</code> from the old one. All in all, the second approach is superior on all axes.</p><h3><a name="the-power-of-parsing"></a>The power of parsing</h3><p>You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:</p><pre><code class="pygments"><span class="nf">validateNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span> + +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="n">xs</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span></code></pre><p>These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: <code>validateNonEmpty</code> always returns <code>()</code>, the type that contains no information, but <code>parseNonEmpty</code> returns <code>NonEmpty a</code>, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but <code>parseNonEmpty</code> gives the caller access to the information it learned, while <code>validateNonEmpty</code> just throws it away.</p><p>These two functions elegantly illustrate two different perspectives on the role of a static type system: <code>validateNonEmpty</code> obeys the typechecker well enough, but only <code>parseNonEmpty</code> takes full advantage of it. If you see why <code>parseNonEmpty</code> is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of <code>parseNonEmpty</code>’s name. Is it really <em>parsing</em> anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe <code>parseNonEmpty</code> is a bona-fide parser (albeit a particularly simple one).</p><p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and <code>parseNonEmpty</code> is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.</p><p>Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:</p><ul><li><p>The <a href="https://hackage.haskell.org/package/aeson">aeson</a> library provides a <code>Parser</code> type that can be used to parse JSON data into domain types.</p></li><li><p>Likewise, <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> provides a set of parser combinators for parsing command-line arguments.</p></li><li><p>Database libraries like <a href="https://hackage.haskell.org/package/persistent">persistent</a> and <a href="https://hackage.haskell.org/package/postgresql-simple">postgresql-simple</a> have a mechanism for parsing values held in an external data store.</p></li><li><p>The <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.</p></li></ul><p>The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.</p><p>One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the <code>NonEmpty</code> example above: if the parsing and processing logic go out of sync, the program will fail to even compile.</p><h3><a name="the-danger-of-validation"></a>The danger of validation</h3><p>Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?</p><p>Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the <a href="http://langsec.org">language-theoretic security</a> field calls <em>shotgun parsing</em>. In the 2016 paper, <a href="http://langsec.org/papers/langsec-cwes-secdev2016.pdf">The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them</a>, its authors provide the following definition:</p><blockquote><p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.</p></blockquote><p>They go on to explain the problems inherent to such validation techniques:</p><blockquote><p>Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.</p></blockquote><p>In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.</p><p>It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.</p><p>Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p><h2><a name="parsing-not-validating-in-practice"></a>Parsing, not validating, in practice</h2><p>So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”</p><p>My advice: focus on the datatypes.</p><p>Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a <code>Map</code>. Adjust your function’s type signature to accept a <code>Map</code> instead of a list of tuples, and implement it as you normally would.</p><p>Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to <code>Map</code>, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of <code>checkNoDuplicateKeys</code>:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span></code></pre><p>Now the check <em>cannot</em> be omitted, since its result is actually necessary for the program to proceed!</p><p>This hypothetical scenario highlights two simple ideas:</p><ol><li><p><strong>Use a data structure that makes illegal states unrepresentable.</strong> Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.</p></li><li><p><strong>Push the burden of proof upward as far as possible, but no further.</strong> Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.</p></li></ol><p>In other words, write functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!</p><p>Here are a handful of additional points of advice, arranged in no particular order:</p><ul><li><p><strong>Let your datatypes inform your code, don’t let your code control your datatypes.</strong> Avoid the temptation to just stick a <code>Bool</code> in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.</p></li><li><p><strong>Treat functions that return <code>m ()</code> with deep suspicion.</strong> Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.</p></li><li><p><strong>Don’t be afraid to parse data in multiple passes.</strong> Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p></li><li><p><strong>Avoid denormalized representations of data, <em>especially</em> if it’s mutable.</strong> Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.</p><ul><li><p><strong>Keep denormalized representations of data behind abstraction boundaries.</strong> If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.</p></li></ul></li><li><p><strong>Use abstract datatypes to make validators “look like” parsers.</strong> Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract <code>newtype</code> with a smart constructor to “fake” a parser from a validator.</p></li></ul><p>As always, use your best judgement. It probably isn’t worth breaking out <a href="https://hackage.haskell.org/package/singletons">singletons</a> and refactoring your entire application just to get rid of a single <code>error "impossible"</code> call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.</p><h2><a name="recap-reflection-and-related-reading"></a>Recap, reflection, and related reading</h2><p>That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.</p><p>None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about <em>process</em>. My hope is that this is a small step in that direction.</p><p>Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post <a href="https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html">Type Safety Back and Forth</a>. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper <a href="https://kataskeue.com/gdp.pdf">Ghosts of Departed Proofs</a>, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.</p><p>As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit <em>any</em> value. These aren’t “real” values (unlike <code>null</code> in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that <a href="https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf">Fast and Loose Reasoning is Morally Correct</a>. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In fact, <code>Data.List.NonEmpty</code> already provides a <code>head</code> function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Rascal: a Haskell with more parentheseshttps://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/https://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/02 Jan 2017<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Four months with Haskellhttps://lexi-lambda.github.io/blog/2016/06/12/four-months-with-haskell/https://lexi-lambda.github.io/blog/2016/06/12/four-months-with-haskell/12 Jun 2016<article><p>At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.</p><p>Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an <em>incredible</em> programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.</p><h2><a name="dispelling-some-myths"></a>Dispelling some myths</h2><p>Before moving on and discussing my experiences in depth, I want to take a quick detour to dispel some frequent rumors I hear about why Haskell is at least potentially problematic. These are things I hear a <em>lot</em>, and nothing in my experience so far would lead me to believe these are actually true. Ultimately, I don’t want to spend too much time on these—I think that, for the most part, they are nitpicks that people complain about to avoid understanding the deeper and more insidious problems with the language—but I think it’s important to at least mention them.</p><h3><a name="hiring-haskell-developers-is-not-hard"></a>Hiring Haskell developers is not hard</h3><p>I am on the first Haskell team in my company, and I am among the first Haskell developers we ever hired. Not only were we hiring without much experience with Haskell at all, we explicitly <em>did not</em> want to hire remote. Debate all you like about whether or not permitting remote work is a good idea, but I don’t think anyone would dispute that this constraint makes hiring much harder. We didn’t have any trouble finding a very large stream of qualified applicants, and it definitely seems to have dispelled any fears that we would have trouble finding new candidates in the future.</p><h3><a name="performing-i-o-in-haskell-is-easy"></a>Performing I/O in Haskell is easy</h3><p>Haskell’s purity is a point of real contention, and it’s one of the most frustrating complaints I often hear about Haskell. It is surprisingly common to hear concerns along the lines of “I don’t want to use Haskell because its academic devotion to purity sounds like it would make it very hard to get anything done”. There are very valid reasons to avoid Haskell, but in practice, I/O is not one of them. In fact, I found that isolating I/O in Haskell was much the same as isolating I/O in every other language, which I need to do anyway to permit unit testing.</p><p>...you <em>do</em> write deterministic unit tests for your impure logic, right?</p><h3><a name="working-with-lots-of-monads-is-not-very-difficult"></a>Working with lots of monads is not very difficult</h3><p>The “M word” has ended up being a running joke <em>about</em> Haskell that actually ends up coming up fairly rarely <em>within</em> the Haskell community. To be clear, there is <em>no doubt</em> in my mind that monads make Haskell intimidating and provide a steep learning curve for new users. The proliferation of the joke that monads are impossible to explain, to the point of becoming mythologized, is absolutely indicative of a deeper problem about Haskell’s accessibility. However, once people learn the basics about monads, I’ve found that applying them is just as natural as applying any other programming pattern.</p><p>Monads are used to assist the programmer, not impede them, and they really do pay off in practice. When something has a monadic interface, there’s a decent chance I already know what that interface is going to do, and that makes working with lots of different monads surprisingly easy. Admittedly, I do rely very, very heavily on tooling to help me out here, but with things like mouseover type tooltips, I’ve actually found that working with a variety of different monads and monad transformers is actually quite pleasant, and it makes things very readable!</p><h2><a name="haskell-the-good-parts"></a>Haskell: the good parts</h2><p>With the disclaimers out of the way, I really just want to gush for a little bit. This is not going to be an objective, reasoned survey of why Haskell is good. I am not even really going to touch upon why types are so great and why purity is so wonderful—I’d love to discuss those in depth, but that’s for a different blog post. For now, I just want to touch upon the real surprises, the real things that made me <em>excited</em> about Haskell in ways I didn’t expect. These are the things that my subjective little experience has found fun.</p><h3><a name="language-extensions-are-haskell"></a>Language extensions <em>are</em> Haskell</h3><p>There was a time in my life when I spent a lot of time writing C. There are a lot of compilers for C, and they all implement the language in subtly different but often incompatible ways, especially on different platforms. The only way to maintain a modicum of predictability was to adhere to the standards <em>religiously</em>, even when certain GCC or MSVC extensions seem tantalizingly useful. I was actually bitten a few times by real instances where I figured I’d just use a harmless extension that was implemented everywhere, then found out it worked slightly differently across different compilers in a particular edge case. It was a learning experience.</p><p>It seems that this fear provides a very real distrust for using GHC’s numerous <em>language extensions</em>, and indeed, for a long time, I felt that it was probably an admirable goal to stick to Haskell 98 or Haskell 2010 as closely as possible. Sometimes I chose a slightly more verbose solution that was standard Haskell to avoid turning on a trivial extension that would make the code look a little bit cleaner.</p><p>About a year later, I’m finding that attitude was not only a mistake, but it forced me to often completely miss out on a lot of Haskell’s core value. GHC <em>won</em>, and now GHC and Haskell are basically synonymous. With that in mind, the portability concerns of language extensions are a bit of a non-issue, and turning them on is a very good idea! Some extensions are more than a little dangerous, so they cannot all be turned on without thinking, but the question is absolutely not “Is using language extensions a good idea?” and more “Is using <em>this</em> language extension a good idea?”</p><p>This is important, and I bring it up for a reason: so much of the awesomeness of Haskell is locked behind language extensions. Turning a lot of these on is one of the main things that made me really start to see how incredibly powerful Haskell actually is.</p><h3><a name="phantom-types"></a>Phantom types</h3><p>I’m going to start out by talking about <em>phantom types</em>, which are a pretty simple concept but a powerful one, and they serve as the foundation for a lot of other cool type-level tricks that can make Haskell extremely interesting. The basic idea of a phantom type is simple; it’s a type parameter that isn’t actually used to represent any particular runtime value:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type represents an id for some kind of value, but although the kind of value is specified in the type as the <code>a</code> type parameter, it isn’t actually used anywhere on the data definition—no matter what <code>a</code> is, an <code>Id</code> is just a piece of text. This makes it possible to write functions that operate on specific kinds of ids, and those invariants will be statically checked by the compiler, even though the runtime representation is entirely identical:</p><pre><code class="pygments"><span class="nf">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span></code></pre><p>Using <code>FlexibleInstances</code>, it’s also possible to create different instances for different kinds of ids. For example, it would be possible to have different <code>Show</code> instances depending on the type of id in question.</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"user #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"post #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span></code></pre><p>This provides a simple framework for encoding entirely arbitrary information into the type system, then asking the compiler to actually check assertions about that information. This is made even more powerful with some other extensions, which I’ll talk about shortly.</p><h3><a name="letting-the-compiler-write-code"></a>Letting the compiler write code</h3><p>One of the things I really dislike, more than most things, is boilerplate. A little bit of boilerplate is fine—even necessary at times—but as soon as I start wondering if a code generator would improve things, I think the programming language has pretty much failed me.</p><p>I write a lot of Racket because, in a sense, Racket is the ultimate boilerplate killer: the macro system is a first-class code generator integrated with the rest of the language, and it means that boilerplate is almost never an issue. Of course, that’s not always true: sometimes a bit of boilerplate <em>is</em> still necessary because macros cannot deduce enough information about the program to generate the code entirely on their own, and in Haskell, some of that information is actually present in the type system.</p><p>This leads to two absolutely incredible extensions, both of which are simple and related, but which actually <em>completely change</em> how I approach problems when programming. These two extensions are <code>GeneralizedNewtypeDeriving</code> and <code>StandaloneDeriving</code>.</p><h4><a name="newtypes-and-type-safety"></a>Newtypes and type safety</h4><p>The basic idea is that “newtypes” are just simple wrapper types in Haskell. This turns out to be extremely important when trying to find the value of Haskell because they allow you to harden type safety by specializing types to <em>your</em> domain. For example, consider a type representing a user’s name:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type is extremely simple, and in fact isn’t even at all different from a simple <code>Text</code> value with respect to its representation, since all combinations of unicode characters are allowed in a name. Therefore, what’s the point of a separate type? Well, this allows Haskell to introduce actual compilation failures when two different kinds of textual data are mixed. This is not a new idea, and even in languages that don’t support this sort of thing, Joel Spolsky’s old blog post <a href="http://www.joelonsoftware.com/articles/Wrong.html">Making Wrong Code Look Wrong</a> describes how it can be done by convention. Still, almost every modern language makes this possible: in C, it would be a single-member <code>struct</code>, in class-based OO languages, it would be a single-member class... this is not a complicated idea.</p><p>The difference lies in its usage. In other languages, this strategy is actually not very frequently employed for the simple reason that it is almost always extremely annoying. You are forced to do tons of wrapping/unwrapping, and at that point it isn’t really clear if you’re even getting all that much value out of the distinction when your first solution to a type mismatch is wrapping or unwrapping the value without a second thought. In Haskell, however, this can be heavily mitigated by asking the compiler to <em>automatically derive typeclass implementations</em>, which allow the unwrapping/wrapping to effectively happen implicitly for a constrained set of operations.</p><h4><a name="using-generalizednewtypederiving"></a>Using <code>GeneralizedNewtypeDeriving</code></h4><p>Consider the <code>Name</code> type once again, but this time, let’s derive a class:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">IsString</span><span class="p">)</span></code></pre><p>The <code>IsString</code> typeclass in Haskell allows custom types to automatically be created from string literals. It is <em>not</em> handled specially by Haskell’s <code>deriving</code> mechanism. Since <code>Text</code> implements <code>IsString</code>, an instance will be generated that simply defers to the underlying type, automatically generating the code to wrap the result up in a <code>Name</code> box at the end. This means that code like this will now just magically work:</p><pre><code class="pygments"><span class="nf">name</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Name</span> +<span class="nf">name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa P. Hacker"</span></code></pre><p>No boilerplate needs to be written! This is a neat trick, but it actually turns out to be far more useful than that simple example in practice. What really makes this functionality shine is when you want to derive <em>some</em> kinds of functionality but disallow some others. For example, using the <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a> package, it’s possible to do something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">ToText</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>This creates an opaque <code>Id</code> type, but it automatically generates conversions <em>to</em> textual formats. However, it does <em>not</em> automatically create <code>FromText</code> or <code>FromJSON</code> instances, which would be dangerous because decoding <code>Id</code>s can potentially fail. It’s then possible to write out those instances manually to preserve a type safety:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">FromText</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fromText</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="n">isValidId</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="kr">then</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">String</span><span class="w"> </span><span class="n">val</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">fromText</span><span class="w"> </span><span class="n">val</span><span class="p">)</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span></code></pre><h4><a name="using-standalonederiving"></a>Using <code>StandaloneDeriving</code></h4><p>The ordinary <code>deriving</code> mechanism is extremely useful, especially when paired with the above, but sometimes it is desirable to have a little bit more flexibility. In these cases, <code>StandaloneDeriving</code> can help.</p><p>Take the <code>Id</code> example again: it has a phantom type, and simply adding something like <code>deriving (ToText)</code> with derive <code>ToText</code> instances for <em>all</em> kinds of ids. It is potentially useful, however, to derive instances for more specific id types. Using standalone <code>deriving</code> constructs permits this sort of flexibility.</p><pre><code class="pygments"><span class="kr">deriving</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toText</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">postIdToText</span></code></pre><p>This is an example where GHC language extensions end up becoming significantly more than the sum of their parts, which seems to be a fairly frequent realization. The <code>StandaloneDeriving</code> mechanism is a little bit useful without <code>GeneralizedNewtypeDeriving</code>, but when combined, they are incredibly powerful tools for getting a very fine-grained kind of type safety <em>without</em> writing any boilerplate.</p><h3><a name="datakinds-are-super-cool-with-caveats"></a>DataKinds are super cool, with caveats</h3><p>Phantom types are quite wonderful, but they can only encode <em>types</em>, not arbitrary data. That’s where <code>DataKinds</code> and <code>KindSignatures</code> come in: they allow lifting arbitrary datatypes to the type level so that things that would normally be purely runtime values can be used at compile-time as well.</p><p>The way this works is pretty simple—when you define a datatype, you also define a “datakind”:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Registered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Anonymous</span></code></pre><p>Normally, the above declaration declares a <em>type</em>, <code>RegistrationStatus</code>, and two <em>data constructors</em>, <code>Registered</code> and <code>Anonymous</code>. With <code>DataKinds</code>, it also defines a <em>kind</em>, <code>RegistrationStatus</code>, and two <em>type constructors</em>, <code>Registered</code> and <code>Anonymous.</code></p><p>If that’s confusing, the way to understand that is to realize there is a sort of natural ordering here: types describe values, and kinds describe types. Therefore, turning on <code>DataKinds</code> “lifts” each definition by a single level, so types become kinds and values become types. This permits using these things at the type level:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>In this example, <code>UserId</code> still has a single phantom type variable, <code>s</code>, but this time it is constrained to the <code>RegistrationStatus</code> kind. Therefore, it can <em>only</em> be <code>Registered</code> or <code>Anonymous</code>. This cooperates well with the aforementioned <code>StandaloneDeriving</code> mechanism, and it mostly provides a convenient way to constrain type variables to custom kinds.</p><p>In general, <code>DataKinds</code> is a much more powerful extension, allowing things like type-level natural numbers or strings, which can be used to perform actual type-level computation (especially in combination with <code>TypeFamilies</code>) or a sort of metaprogramming. In some cases, they can even be used to implement things emulating things you can do with dependent types.</p><p>I think <code>DataKinds</code> are a very cool Haskell extension, but there are a couple caveats. One of the main ones is how new kinds are defined: <code>DataKinds</code> “hijacks” the existing datatype declaration syntax by making every single datatype declaration define a type <em>and</em> a kind. This is a little confusing, and it would be nice if a different syntax was used so that each could be defined independently.</p><p>Similarly, it seems that a lot of work is being done to allow using runtime values at the type level, but I wonder if people will ever need to use, say, runtime values at the <em>kind</em> level. This immediately evokes thoughts of Racket’s phase-based macro system, and I wonder if some of this duplication would be unnecessary with something similar.</p><p>Food for thought, but overall, <code>DataKinds</code> are a very nice addition to help with precisely and specifically typing particular problems.</p><h3><a name="typeclasses-can-emulate-effects"></a>Typeclasses can emulate effects</h3><p>This is something that I’ve found interesting in my time writing Haskell because I have <em>no idea</em> if it’s idiomatic or not, but it seems pretty powerful. The initial motivator for this idea was figuring out how to test our code without constantly dropping into <code>IO</code>.</p><p>More generally, we wanted to be able to unit test by “mocking” out collaborators, as it would be described in object oriented programming. I was always semi-distrustful of mocking, and indeed, it seems likely that it is heavily abused in certain circles, but I’ve come to appreciate the need that sometimes it is important to stub things out, <em>even in pure code</em>.</p><p>As an example, consider some code that needs access to the current time. This is something that would normally require <code>IO</code>, but we likely want to be able to use the value in a pure context without “infecting” the entire program with <code>IO</code> types. In Haskell, I have generally seen three ways of handling this sort of thing:</p><ol><li><p>Just inject the required values into the function and produce them “higher up” where I/O is okay. If threading the value around becomes too burdensome, use a Reader monad.</p></li><li><p>Use a free monad or similar to create a pure DSL of sorts, then write interpreters for various implementations, one of which uses <code>IO</code>.</p></li><li><p>Create custom monadic typeclasses that provide interfaces to the functionality you want to perform, then create instances, one of which is an instance over <code>IO</code>.</p></li></ol><p>This last approach seems to be less common in Haskell, but it’s the approach we took, and it seems to work out remarkably well. Returning to the need to get the current time, we could pretty easily write such a typeclass to encode that need:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">UTCTime</span></code></pre><p>Now we can write functions that use the current time:</p><pre><code class="pygments"><span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span><span class="p">)</span></code></pre><p>Now, we can write instances for <code>CurrentTime</code> that will allow us to run the same code in different contexts:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runAppM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">runTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">runTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftIO</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Time</span><span class="o">.</span><span class="kt">Clock</span><span class="o">.</span><span class="n">getCurrentTime</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">posixSecondsToUTCTime</span><span class="w"> </span><span class="mi">0</span></code></pre><p>Where this really starts to shine is when adding additional effects. For example, the above token validation function might also need information about some kind of secret used for signing. Under this model, it’s just another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getTokenSecret</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Secret</span> + +<span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">secret</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getTokenSecret</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span> +<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">verifySignature</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="n">secret</span><span class="p">)</span></code></pre><p>Of course, so far all of these functions have been extremely simple, and we’ve basically been using them as a glorified reader monad. In practice, though, we use this pattern for lots more than just retrieving values. For example, we might have a typeclass for database interactions:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> +<span class="w"> </span><span class="n">insertUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">PersistenceError</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">))</span></code></pre><p>With all of this done, it becomes incredibly easy to see which functions are using which effects:</p><pre><code class="pygments"><span class="nf">postUsers</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">postUsers</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">getHealthcheck</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">getHealthcheck</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>There’s no need to perform any lifting, and this all seems to scale quite nicely. We’ve written some additional utilities to help write tests against functions using these kinds of monadic interfaces, and even though there’s a little bit of annoying boilerplate in a few spots, overall it seems to work quite elegantly.</p><p>I’m not entirely sure how common this is in the Haskell community, but it’s certainly pretty neat how easy it is to get nearly all of the benefits of effect types in other languages simply by composing some of Haskell’s simplest features.</p><h3><a name="atom-s-ide-haskell-tooling-is-invaluable"></a>Atom’s ide-haskell tooling is invaluable</h3><p>Alright, so, confession time: I don’t use Emacs.</p><p>I know, I know, how is that possible? I write Lisp, after all. Well, honestly, I tried picking it up a number of times, but none of those times did I get far enough to ditch my other tools. For Racket work, I use DrRacket, but for almost everything else, I use Atom.</p><p>Atom has a lot of flaws, but it’s also pretty amazing in places, and I absolutely <em>love</em> the Haskell tooling written by the wonderful <a href="https://github.com/atom-haskell">atom-haskell</a> folks. I use it constantly, and even though it doesn’t always work perfectly, it works pretty well. When it has problems, I’ve at least figured out how to get it working correctly.</p><p>This is probably hard to really explain without seeing it for yourself, but I’ve found that I basically <em>depend</em> on this sort of tooling to be fully productive in Haskell, and I have no problem admitting that. The ability to get instant feedback about type errors tied to visual source locations, to be able to directly manipulate the source by selecting expressions and getting type information, and even the option to get inline linter suggestions means I spend a lot less time glancing at the terminal, and even less time in the REPL.</p><p>The tooling is far from perfect, and it leaves a lot to be desired in places (the idea of using that static information for automated, project-wide refactoring <em>a la</em> Java is tantalizing), but most of those things are ideas of what amazing things could be, not broken or missing essentials. I am pretty satisfied with ide-haskell right now, and I can only hope it continues to get better and better.</p><h2><a name="frustrations-drawbacks-and-pain-points"></a>Frustrations, drawbacks, and pain points</h2><p>Haskell is not perfect. In fact, far from it. There is a vast array of little annoyances that I have with the language, as is the case with any language. Still, there are a few overarching problems that I would really like to at least mention. These are the biggest sources of frustration for me so far.</p><h3><a name="purity-failure-and-exception-handling"></a>Purity, failure, and exception-handling</h3><p>One of Haskell’s defining features is its purity—I don’t think many would disagree with that. Some people consider it a drawback, others consider it one of its greatest boons. Personally, I like it a lot, and I think one of the best parts about it is how it requires the programmer to be incredibly deliberate about failure.</p><p>In many languages, when looking up a value from a container where the key doesn’t exist, there are really two ways to go about expressing this failure:</p><ol><li><p>Throw an exception.</p></li><li><p>Return <code>null</code>.</p></li></ol><p>The former is scary because it means <em>any</em> call to any function can make the entire program blow up, and it’s often impossible to know which functions even have the potential to throw. This creates a certain kind of non-local control flow that can sometimes cause a lot of unpredictability. The second option is much the same, especially when any value in a program might be <code>null</code>; it just defers the failure.</p><p>In languages with option types, this is somewhat mitigated. Java now has option types, too, but they are still frequently cumbersome to use because there is nothing like monads to use to simply chain operations together. Haskell, in comparison, has an incredible complement of tools to simply handle errors without a whole lot of burden on the programmer, and I have found that, in practice, this is <em>actually helpful</em> and I really do write better error-handling code.</p><h4><a name="first-the-good-parts"></a>First, the good parts</h4><p>I have seen a comparison drawn between throwing checked exceptions and returning <code>Maybe</code> or <code>Either</code> types, but in practice the difference is massive. Handling checked exceptions is a monotonous chore because they are not first-class values, they are actually entirely separate linguistic constructs. Consider a library that throws a <code>LibraryException</code>, and you want to wrap that library and convert those exceptions to <code>ApplicationException</code>s. Well, have fun writing this code dozens of times:</p><pre><code class="pygments"><span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomething</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span> + +<span class="c1">// ...</span> + +<span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomethingElse</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span></code></pre><p>In Haskell, failure is just represented by first-class values, and it’s totally possible to write helper functions to abstract over that kind of boilerplate:</p><pre><code class="pygments"><span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ApplicationError</span> +<span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">ApplicationError</span><span class="w"> </span><span class="n">a</span> +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapLeft</span><span class="w"> </span><span class="n">libraryToApplication</span></code></pre><p>Now, that same boilerplate-y code becomes nearly invisible:</p><pre><code class="pygments"><span class="nf">x</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomething</span> + +<span class="c1">-- ...</span> + +<span class="nf">y</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomethingElse</span></code></pre><p>This might not <em>seem</em> like much, but it really cuts down on the amount of visual noise, which ends up making all the difference. Boilerplate incurs a cost much bigger than simply taking the time to type it all out (though that’s important, too): the cognitive overhead of parsing which parts of a program are boilerplate has a significant impact on readability.</p><h4><a name="so-what-s-the-problem"></a>So what’s the problem?</h4><p>If error handling is so great in Haskell, then why am I putting it under the complaints section? Well, it turns out that not everyone seems to think it’s as great as I make it out to be because people seem to keep writing Haskell APIs that throw exceptions!</p><p>Despite what some purists would have you believe, Haskell has exceptions, and they are not uncommon. Lots of things can throw exceptions, some of which are probably reasonable. Failing to connect to a database is a pretty catastrophic error, so it seems fair that it would throw. On the other hand, inserting a duplicate record is pretty normal operation, so it seems like that should <em>not</em> throw.</p><p>I mostly treat exceptions in Haskell as unrecoverable catastrophes. If I throw an error in <em>my</em> code, I do not intend to catch it. That means something horrible happened, and I just want that horribleness to show up in a log somewhere so I can fix the problem. If I care about failure, there are better ways to handle that failure gracefully.</p><p>It’s also probably worth noting that exceptions in Haskell can be thrown from anywhere, even pure code, but can only be <em>caught</em> within the <code>IO</code> monad. This is especially scary, but I’ve seen it happen in actual libraries out in the wild, even ones that the entire Haskell ecosystem is built on. One of the crowning examples of this is the <code>text</code> package, which provides a function called <code>decodeUtf8</code> to convert bytestrings into text. Its type is very simple:</p><pre><code class="pygments"><span class="nf">decodeUtf8</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ByteString</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>But wait, what if the bytestring is not actually a valid UTF-8 string?</p><p>Boom. There goes the application.</p><p>Okay, okay, well, at least the <code>text</code> package provides another function, this one called <code>decodeUtf8'</code>, which returns an <code>Either</code>. This is good, and I’ve trained myself to only ever use <code>decodeUtf8'</code>, but it still has some pretty significant problems:</p><ul><li><p>The <em>safe</em> version of this function is the “prime” version, rather than the other way around, which encourages people to use the unsafe one. Ideally, the unsafe one should be explicitly labeled as such... maybe call it <code>unsafeDecodeUtf8</code>?</p></li><li><p>This is not a hypothetical problem. When using a Haskell JWT library, we found a function that converts a string into a JWT. Since not all strings are JWTs, the library intelligently returns a <code>Maybe</code>. Therefore, we figured we were safe.</p><p>A couple weeks later, we found that providing this function with invalid data was returning HTTP 500 errors. Why? Our error handling was meticulous! Well, the answer was a <code>decodeUtf8</code> call, hidden inside of the JWT library. This is especially egregious, given that the API it exposed returned a <code>Maybe</code> anyway! It would have been trivial to use the safe version there, instead, but the poor, misleading name led the library developer to overlook the bug lurking in the otherwise innocuous function.</p><p>Even worse, this function was totally pure, and we used it in pure code, so we could not simply wrap the function and catch the exception. We had two options: use <code>unsafePerformIO</code> (yuck!) or perform a check before handing the data to the buggy function. We chose the latter, but in some cases, I imagine that could be too difficult to do in order to make it feasible.</p></li></ul><p>The point I’m trying to make is that this is a real problem, and it seems to me that throwing exceptions invalidates one of the primary advantages of Haskell. It disappointed me to realize that a significant amount of code written by FP Complete, one of the primary authors of some of the most important “modern Haskell” code in existence (including Stack), seem to very frequently expose APIs that will throw.</p><p>I’m not sure how much of this stems from a fundamental divide in the Haskell ecosystem and how much it is simply due to Michael Snoyman’s coding style, given that he is the primary author of a number of these tools and libraries that seem very eager to throw exceptions. As just one example of a real situation in which we were surprised by this behavior, we used Snoyman’s http-client library and found that it mysteriously throws upon nearly <em>any</em> failure state:</p><blockquote><p>A note on exceptions: for the most part, all actions that perform I/O should be assumed to throw an <code>HttpException</code> in the event of some problem, and all pure functions will be total. For example, <code>withResponse</code>, <code>httpLbs</code>, and <code>BodyReader</code> can all throw exceptions.</p></blockquote><p>This doesn’t seem entirely unreasonable—after all, isn’t a failure to negotiate TLS fairly catastrophic?—until you consider our use case. We needed to make a subrequest during the extent of another HTTP request to our server, and if that subrequest fails, we absolutely need to handle that failure gracefully. Of course, this is not <em>terrible</em> given that we are in <code>IO</code> so we can actually catch these exceptions, but since this behavior was only noted in a single aside at the top of the documentation, we didn’t realize we were forgetting error handling until far too late and requests were silently failing.</p><p>Exceptions seem to devalue one of the most powerful concepts in Haskell: if I don’t consider all the possibilities, my code <em>does not compile</em>. In practice, when working with APIs that properly encode these possibilities into the type system, this value proposition seems to be real. I really do find myself writing code that works correctly as soon as it compiles. It’s almost magical.</p><p>Using exceptions throws that all out the window, and I wish the Haskell ecosystem was generally more cautious about when to use them.</p><h3><a name="the-string-problem"></a>The String problem</h3><p>I sort of alluded to this a tiny bit in the last section, and that is probably indicative of how bad this issue is. I’m just going to be blunt: <strong>In Haskell, strings suck.</strong></p><p>This is always a bit of an amusing point whenever it is discussed because of how silly it seems. Haskell is a research language with a cutting-edge type system and some of the fanciest features of any language in existence. When everyday programming might use things like “profunctors”, “injective type families”, and “generalized algebraic datatypes”, you would think that dealing with <em>strings</em> would be a well-solved problem.</p><p>But it isn’t. Haskell libraries frequently use not one, not two, but <em><strong>five</strong></em> kinds of strings. Let’s list them off, shall we?</p><ul><li><p>First off, there’s the built-in <code>String</code> type, which is actually an alias for the <code>[Char]</code> type. For those not intimately familiar with Haskell, that’s a <em>linked list of characters</em>. As <a href="http://www.stephendiehl.com/">Stephen Diehl</a> recently put it in <a href="http://www.stephendiehl.com/posts/strings.html">a blog post describing the disaster that is Haskell string types</a>:</p><blockquote><p>This is not only a bad representation, it’s quite possibly the least efficient (non-contrived) representation of text data possible and has horrible performance in both time and space. <em>And it’s used everywhere in Haskell.</em></p></blockquote><p>The point is, it’s really bad. This type is not a useful representation for textual data in practical applications.</p></li><li><p>Moving on, we have a fairly decent type, <code>Text</code>, which comes from <code>Data.Text</code> in the <code>text</code> package. This is a decent representation of text, and it’s probably the one that everything should use. Well, maybe. Because <code>Text</code> comes in two varieties: lazy and strict. Nobody seems to agree on which of those two should be used, though, and they are totally incompatible types: functions that work with one kind of text won’t work with the other. You have to manually convert between them.</p></li><li><p>Finally, we have <code>ByteString</code>, which is horribly misnamed because it really isn’t a string at all, at least not in the textual sense. A better name for this type would have simply been <code>Bytes</code>, which sounds a lot scarier. And that would be good, because data typed as a <code>ByteString</code> is as close as you can get in Haskell to not assigning a type at all: a bytestring holds arbitrary bytes without assigning them any meaning whatsoever!</p><p>Or at least, that’s the intention. The trouble is that people <em>don’t</em> treat bytestrings like that—they just use them to toss pieces of text around, even when those pieces of text have a well-defined encoding and represent textual data. This leads to the <code>decodeUtf8</code> problem mentioned above, but it’s bigger than that because it often ends up with some poor APIs that assign some interpretation to <code>ByteString</code> data without assigning it a different type.</p><p>Again, this is throwing away so much of Haskell’s safety. It would be like using <code>Int</code> to keep track of boolean data (“just use 0 and 1!”) or using empty and singleton lists instead of using <code>Maybe</code>. When you use the precise type, you encode invariants and contracts into statically-checked assertions, but when you use general types like <code>ByteString</code>, you give that up.</p><p>Oh, and did I mention that <code>ByteString</code>s also come in incompatible lazy and strict versions, too?</p></li></ul><p>So, obviously, the answer is to just stop using the bad types and to just use (one kind of) <code>Text</code> everywhere. Great! Except that the other types are totally inescapable. The entire standard library uses <code>String</code> exclusively—after all, <code>text</code> is a separate package—and small libraries often use <code>String</code> instead of <code>text</code> because they have no need to bring in the dependency. Of course, this just means every real application pays the performance hit of converting between all these different kinds of strings.</p><p>Similarly, those that <em>do</em> use <code>Text</code> often use different kinds of text, so code ends up littered with <code>fromStrict</code> or <code>toStrict</code> coercions, which (again) have a cost. I’ve already ranted enough about <code>ByteString</code>, but basically, if you’re using <code>ByteString</code> in your API to pass around data that is semantically text, you are causing me pain. Please stop.</p><p>It seems that the way <code>Data.Text</code> probably <em>should</em> have been designed was by making <code>Text</code> a typeclass, then making the lazy and strict implementations instances of that typeclass. Still, the fact that both of them exist would always cause problems. I’m actually unsure which one is the “correct” choice—I don’t know enough about how the two perform in practice—but it seems likely that picking <em>either</em> one would be a performance improvement over the current system, which is constantly spending time converting between the two.</p><p>This issue has been ranted about plenty, so I won’t ramble on, but if you’re designing new libraries, please, <em>please</em> use <code>Text</code>. Your users will thank you.</p><h3><a name="documentation-is-nearly-worthless"></a>Documentation is nearly worthless</h3><p>Finally, let’s talk about documentation.</p><p>One of my favorite programming languages is Racket. Racket has a documentation tool called Scribble. Scribble is special because it is a totally separate domain-specific language for writing documentation, and it makes it fun and easy to write good explanations. There are even forms for typesetting automatically-rendered examples that look like a REPL. If the examples ever break or become incorrect, the docs don’t even compile.</p><p>All of the Racket core library documentation makes sure to set a good example about what good documentation should look like. The vast majority of the documentation is paragraphs of prose and simple but practical examples. There are also type signatures (in the form of contracts), and those are super important, but they are so effective because of how the prose explains what each function does, when to use it, <em>why</em> you’d use it, and <em>why you wouldn’t use it</em>.</p><p>Everything is cross-referenced automatically. The documentation is completely searchable locally out of the box. As soon as you install a package, its docs are automatically indexed. User-written libraries tend to have pretty good docs, too, because the standard libraries set such a good example <em>and</em> because the tools are so fantastic. Racket docs are really nice, and they’re so good they actually make things like Stack Overflow or even Google mostly irrelevant. It’s all there in the manual.</p><p>Haskell documentation is the opposite of everything I just said.</p><ul><li><p>The core libraries are poorly documented. Most functions include a sentence of description, and almost none include examples. At their worst, the descriptions simply restate the type signature.</p></li><li><p>Third-party libraries’ documentation is even worse, going frequently completely undocumented and actually only including type signatures and nothing else.</p></li><li><p>Haddock is an incredibly user-hostile tool for writing anything other than tiny snippets of documentation and is not very good at supporting prose. Notably, Haddock’s documentation is not generated using Haddock (and it still manages to be almost unusable). Forcing all documentation into inline comments makes users unlikely to write much explanation, and there is no ability for abstraction.</p></li><li><p>Reading documentation locally is very difficult because there is no easy way to open documentation for a particular package in a web browser, and it’s <em>certainly</em> not searchable. This is especially ridiculous given that Hoogle exists, which is one of best ways to search API docs in existence. There should be a <code>stack hoogle</code> command that just opens a Hoogle page for all locally-installed packages and Just Works, but there isn’t.</p></li><li><p>Most valuable information exists outside of documentation, so Google becomes a go-to immediately after a quick glance at the docs, and information is spread across blog posts, mailing lists, and obscure reddit posts.</p></li></ul><p>This is a problem that cannot be fixed by just making Haddock better, nor can it be fixed simply by improving the existing standard library documentation. There is a fundamental problem with Haskell documentation (which, to be completely fair, is not unique to Haskell), which is that its tools do not support anything more than API docs.</p><p>Good documentation is so much more than “here’s what this function does”; it’s about guides and tutorials and case studies and common pitfalls. <a href="http://docs.racket-lang.org/lens/lens-guide.html">This is documentation for someone new to lenses.</a> <a href="https://hackage.haskell.org/package/lens#readme">This is not.</a> Take note of the difference.</p><h2><a name="conclusion-and-other-thoughts"></a>Conclusion and other thoughts</h2><p>Haskell is an incredible programming platform, and indeed, it is sometimes mind-boggling how complete it is. It also has a lot of rough edges, sometimes in places that feel like they need a lot more care, or perhaps they’re even simply unfinished.</p><p>I could spend weeks writing about all the things I really like or dislike about the language, discussing in fine detail all the things that have made me excited or all the little bits that have made me want to tear my hair out. Heck, I could probably spend a month writing about strings alone. That’s not the point, though... I took a risk with Haskell, and it’s paid off. I’m not yet sure exactly how I feel about it, or when I would chose it relative to other tools, but it is currently very high on my list of favorite technologies.</p><p>I did not come to Haskell with a distaste for static typing, despite the fact that I write so much Racket, a dynamically typed language (by default, at least). I don’t really use Typed Racket, and despite my love for Haskell and its type system, I am not sure I will use much more of it than I did before. Haskell and Racket are very different languages, which is justified in some places and probably sort of circumstantial in others.</p><p>The future of Haskell seems bright, and a lot of the changes in the just-released GHC 8 are extremely exciting. I did not list records as a pain point because the changes in GHC 8 appear to make them a <em>lot</em> more palatable, although whether or not they solve that problem completely remains to be seen. I will absolutely continue to write Haskell and push it to its limits where I can, and hopefully try and take as much as I can from it along the way.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/hackett.atom.xml b/feeds/hackett.atom.xml new file mode 100644 index 0000000..7ea3476 --- /dev/null +++ b/feeds/hackett.atom.xml @@ -0,0 +1,650 @@ +Posts tagged ‘hackett’ | Alexis King’s Blog2018-04-15T00:00:00ZReimplementing Hackett’s type language: expanding to custom core forms in Racket2018-04-15T00:00:00Z2018-04-15T00:00:00ZAlexis King<article><p>In the past couple of weeks, I <a href="https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935">completely rewrote the implementation of Hackett’s type language</a> to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.</p><p>This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">my previous blog post on Hackett</a>, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.</p><h2><a name="what-are-core-forms"></a>What are core forms?</h2><p>Before we can get started writing <em>custom core forms</em>, we need to understand the meaning of Racket’s plain old <em>core forms</em>. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.</p><p>To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a <code>#lang</code> line at the top. In this case, we’ll use <code>#lang racket</code> to keep things simple:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as <em>reading</em> the program. The <code>#lang</code> line controls how the program is read—some <code>#lang</code>s provide parsers that allow syntax that is very different from the parser used for <code>#lang racket</code>—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span></code></pre><p>Note the introduction of <code>#%module-begin</code>. Despite the fancy name, this is really just an ordinary macro provided by the <code>racket</code> language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with <code>#%module-begin</code>; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.</p><p>One the program has been read, it is subsequently <em>expanded</em> by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">2</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">call-with-values</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="n">add2</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">))</span> +<span class="w"> </span><span class="n">print-values</span><span class="p">)))</span></code></pre><p>Let’s note the things that changed:</p><ol><li><p><code>#%module-begin</code> was replaced with <code>#%plain-module-begin</code>. <code>#%plain-module-begin</code> is a binding that wraps the body of every expanded module, and all definitions of <code>#%module-begin</code> in any language must eventually expand to <code>#%plain-module-begin</code>. However, <code>#lang racket</code>’s <code>#%module-begin</code> doesn’t <em>just</em> expand to <code>#%plain-module-begin</code>, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints <code>5</code> even though there is no code related to printing in the original program!</p></li><li><p>The lambda shorthand used with <code>define</code> was converted to an explicit use of <code>lambda</code>, and it was expanded to <code>define-values</code>. In Racket, <code>define</code> and <code>define-syntax</code> are really just macros for <code>define-values</code> and <code>define-syntaxes</code> that only bind a single identifier.</p></li><li><p>All function applications were tagged explicitly with <code>#%plain-app</code>. This syntactically distinguishes function applications from uses of forms like <code>define-values</code> or <code>lambda</code>. It also allows languages to customize function application by providing their own macros named <code>#%app</code> (just like languages can provide their own macros named <code>#%module-begin</code> that expand to <code>#%plain-module-begin</code>), but that is outside the scope of this blog post.</p></li><li><p>All literals have been wrapped with <code>quote</code>, so <code>2</code> became <code>'2</code> and <code>3</code> became <code>'3</code>.</p></li></ol><p>Importantly, the resulting program contains <strong>no macros</strong>. Such programs are called <em>fully expanded</em>, since all macros have been eliminated and no further expansion can take place.</p><p>So what’s left behind? Well, some of the things in the program are literal data, like the numbers <code>2</code> and <code>3</code>. There are also some variable references, <code>x</code> and <code>add2</code>. Most of the program, however, is built out of primitives like <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, and <code>lambda</code>. These primitives are <em>core forms</em>—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.</p><p>In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:</p><pre><code class="pygments"><span class="n">var</span> <span class="n">add2</span> <span class="o">=</span> + <span class="n">function</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span> + +<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">add2</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span></code></pre><p>Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be <em>compiled</em>. The Racket compiler has built-in rules for how to compile core forms like <code>define-values</code>, <code>lambda</code>, and <code>#%plain-app</code>, and the result is optimized Racket bytecode.</p><p>In the remainder of this blog post, as most discussions of macros do, we’ll ignore the <em>read</em> and <em>compile</em> steps of the Racket program pipeline and focus exclusively on the <em>expand</em> step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.</p><h3><a name="racket-s-default-core-forms"></a>Racket’s default core forms</h3><p>So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, <code>lambda</code>, and <code>quote</code>, but there are many more. The full list is available in the section of the Racket reference named <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29">Fully Expanded Programs</a>, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like <code>define-syntaxes</code>, <code>if</code>, <code>let-values</code>, <code>letrec-values</code>, <code>begin</code>, <code>quote-syntax</code>, and <code>set!</code>. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.</p><p>These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, <code>#lang racket</code> implements <code>cond</code> as a macro that eventually expands into <code>if</code>, and it implements <code>syntax</code> as a macro that eventually expands into function calls and <code>quote-syntax</code>. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s <code>match</code> can expand into uses of <code>let</code> and <code>cond</code>, and it doesn’t need to concern itself with using <code>let-values</code> and <code>if</code>. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.</p><h3><a name="the-need-for-custom-core-forms"></a>The need for custom core forms</h3><p>With that in mind, why might we wish to define <em>custom</em> core forms? In fact, what would such a thing even mean? By their very nature, <em>all</em> Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.</p><p>Despite this, there <em>are</em> at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.</p><h4><a name="supporting-multiple-backends"></a>Supporting multiple backends</h4><p>The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket <code>#lang</code> that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might <em>also</em> wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.</p><p>In the case of a hardware description language, the custom core forms might include things like <code>input</code> and <code>output</code> for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could <em>additionally</em> define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.</p><p>Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.</p><h4><a name="compiling-an-extensible-embedded-language"></a>Compiling an extensible embedded language</h4><p>A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.</p><p>This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it <em>will</em> eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.</p><p>Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the <code>match</code> macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.</p><p>Existing DSLs of this type are rare, but they do exist. <code>syntax/parse</code> provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from <code>racket/match</code> to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While <code>match</code> is essentially just a traditional pattern-matcher, albeit an extensible one, <code>syntax-parse</code> is its own programming language, closer in some ways to Prolog than to Racket.</p><p>For this reason, <code>syntax/parse</code> has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as <a href="http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29"><em>pattern directives</em></a>. Here is an example of pattern directives in action, from my own <code>threading</code> library:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>Each directive is represented by a keyword, in this case <code>#:do</code> and <code>#:with</code>. Each directive has a corresponding keyword in the pattern language, in this case <code>~do</code> and <code>~parse</code>. Therefore, the above pattern could equivalently be written this way:</p><pre><code class="pygments"><span class="p">[{</span><span class="n">~and</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">~do</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using <code>#:declare</code>, so this is also equivalent:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">expr</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a <code>#:with</code> or <code>~parse</code> pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using <code>#:do</code> or <code>~do</code>, practical uses of <code>syntax/parse</code> really do involve quite a lot of programming in its pattern DSL.</p><p>But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—<code>#:declare</code>, <code>#:do</code>, and <code>#:with</code>, among others—are essentially the core forms of <code>syntax/parse</code>’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.</p><p>But what if <code>syntax/parse</code> could define its own core forms? What if, instead of <code>#:do</code>, <code>#:declare</code>, and <code>#:with</code> being implemented as keyword options specially recognized by the <code>syntax-parse</code> grammar, it defined <code>do</code>, <code>declare</code>, and <code>with</code> as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the <code>syntax/parse</code> core forms. The implementation of <code>syntax/parse</code> could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.</p><p>Now, to be fair, <code>syntax/parse</code> is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.</p><p>The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.</p><h2><a name="the-need-for-a-custom-type-language-in-hackett"></a>The need for a custom type language in Hackett</h2><p>In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for <em>both</em> of them:</p><ul><li><p>Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.</p></li><li><p>Hackett can <em>also</em> make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.</p></li></ul><p>The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:</p><ul><li><p><code>(#%type:con <em>id</em>)</code> — Type constructors, like <code>Integer</code> or <code>Maybe</code>. These are one of the fundamental building blocks of Hackett types.</p></li><li><p><code>(#%type:app <em>type</em> <em>type</em>)</code> — Type application, such as <code>(Maybe Integer)</code>. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of <code>#%type:app</code>.</p></li><li><p><code>(#%type:forall <em>id</em> <em>type</em>)</code> — Universal quantification. This is essentially a binding form, which binds any uses of <code>(#%type:bound-var <em>id</em>)</code> in <code><em>type</em></code>.</p></li><li><p><code>(#%type:qual <em>type</em> <em>type</em>)</code> — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like <code>Eq</code> are bound as type constructors.</p></li><li><p>Finally, Hackett types support three different varieties of type variables:</p><ul><li><p><code>(#%type:bound-var <em>id</em>)</code> — Bound type variables. These are only legal under a corresponding <code>#%type:forall</code>.</p></li><li><p><code>(#%type:wobbly-var <em>id</em>)</code> — Solver variables, which may unify with any other type as part of the typechecking process.</p></li><li><p><code>(#%type:rigid-var <em>id</em>)</code> — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.</p></li></ul></li></ul><p>To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we <em>can</em> bind them to macros that do nothing but raise an exception if something attempts to expand them:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span></code></pre><p>This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.</p><h3><a name="expanding-types-in-our-type-language"></a>Expanding types in our type language</h3><p>We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we <em>do</em> want to expand some of their subforms. For example, in the type <code>(#%type:app a b)</code>, we want to recursively expand <code>a</code> and <code>b</code>.</p><p>In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a>, and it has an option relevant to our needs: the stop list.</p><p>Often, <code>local-expand</code> is used to force the expander to completely, recursively expand a form. For example, by using <code>local-expand</code>, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="c1">; =&gt; (let-values ([(x) &#39;1]) (#%plain-app + x &#39;2))</span></code></pre><p>The third argument to <code>local-expand</code> is the <em>stop list</em>, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; =&gt; (#%type:forall x t)</span></code></pre><p>Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the <code>#%type:forall</code> identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding <code>t</code> since the expander has no way of knowing which pieces of <code>(#%type:forall x t)</code> it should expand! In this case, we want it to recur to expand <code>t</code>, since it should be a type, but not <code>x</code>, since <code>#%type:forall</code> essentially puts <code>x</code> in binding position.</p><p>Therefore, we have to get more clever. We need to call <code>local-expand</code> to produce a type, then we have to pattern-match on it and subsequently call <code>local-expand</code> <em>again</em> on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.</p><p>One good way to do this is to use <code>syntax/parse</code> syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using <code>local-expand</code> and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled <code>#%type:con</code> and <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:expanded-type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]))</span></code></pre><p>This blog post is definitely <em>not</em> a <code>syntax/parse</code> tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named <code>expansion</code>. This attribute contains the fully expanded version of the type currently being parsed. In the <code>#%type:con</code> case, <code>expansion</code> is just <code>this-syntax</code>, which holds the current piece of syntax being parsed. This makes sense, since uses of <code>#%type:con</code> just expand to themselves—expanding <code>(#%type:con Maybe)</code> should not perform any additional expansion on <code>Maybe</code>. This is one of Hackett’s atomic types.</p><p>In contrast, <code>#%type:app</code> <em>does</em> recursively expand its arguments. By annotating its two subforms with <code>:type</code>, the <code>type</code> syntax class will invoke <code>local-expand</code> on each subform, which will in turn use <code>expanded-type</code> to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once <code>a</code> and <code>b</code> have been expanded, <code>#%type:app</code> reassembles them into a new syntax object using <code>#'(#%type:app a.expansion b.expansion)</code>, which replaces their unexpanded versions with their new, expanded versions.</p><p>We can see this behavior by writing a small <code>expand-type</code> function that will expand its argument:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>Now we can use it to observe what happens when we try expanding a type using <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; #%type:app: expected type</span> +<span class="c1">; at: Maybe</span> +<span class="c1">; in: (#%type:app Maybe Integer)</span></code></pre><p>Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined <code>Maybe</code> or <code>Integer</code> anywhere. Let’s do so! We can define them as simple macros that expand into uses of <code>#%type:con</code>, which can be done easily using <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29"><code>make-variable-like-transformer</code></a> from <code>syntax/transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Maybe</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Integer</span><span class="p">)))</span></code></pre><p>Now, if we try expanding that same type again:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Integer))</span></code></pre><p>…it works! Neat. Now we just need to add the cases for the remaining forms in our type language:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">t:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like <code>let-syntax</code> to work in types. For example, we ought to be able to create a local type binding with <code>let-syntax</code> and have it just work. Unfortunately, it doesn’t:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; let-syntax: expected one of these identifiers: `#%type:con&#39;, `#%type:app&#39;, `#%type:forall&#39;, `#%type:qual&#39;, `#%type:bound-var&#39;, `#%type:wobbly-var&#39;, or `#%type:rigid-var&#39;</span> +<span class="c1">; at: letrec-syntaxes+values</span> +<span class="c1">; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))</span></code></pre><p>What went wrong? And why is it complaining about <code>letrec-syntaxes+values</code>? Well, if you read the documentation for <code>local-expand</code>, you’ll find that its behavior is a little more complicated than you might at first believe:</p><blockquote><p>If <em><code>stop-ids</code></em> is [a nonempty list containing more than just <code>module*</code>], then <code>begin</code>, <code>quote</code>, <code>set!</code>, <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, <code>if</code>, <code>begin0</code>, <code>with-continuation-mark</code>, <code>letrec-syntaxes+values</code>, <code>#%plain-app</code>, <code>#%expression</code>, <code>#%top</code>, and <code>#%variable-reference</code> are implicitly added to <em><code>stop-ids</code></em>. Expansion stops when the expander encounters any of the forms in <em><code>stop-ids</code></em>, and the result is the partially-expanded form.</p></blockquote><p>That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with <code>letrec-syntaxes+values</code> (which <code>let-syntax</code> expands to) is a reasonable one. If the expander naïvely expanded <code>letrec-syntaxes+values</code> in the presence of a nonempty stop list, it could cause some significant problems!</p><p>Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">))</span></code></pre><p>We see <code>let-syntax</code>, so we start by evaluating the expression on the right hand side of the <code>Bool</code> binding. This produces a transformer expression, so we bind <code>Bool</code> to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:</p><pre><code class="pygments"><span class="c1">; local bindings:</span> +<span class="c1">; Bool -&gt; #&lt;variable-like-transformer&gt;</span> +<span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)</span></code></pre><p>Now, the identifier in application position is <code>#%type:app</code>, and <code>#%type:app</code> is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the <code>let-syntax</code> needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is <code>(#%type:app Maybe Bool)</code>. But this is a problem, because when we then go to expand <code>Bool</code>, <code>Bool</code> isn’t in the local binding table anymore! The <code>let-syntax</code> was already erased, and <code>Bool</code> is unbound!</p><p>When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.</p><p>Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call <code>local-expand</code>. The bad news is that first-class definition contexts are <em>complicated</em>, and using them properly is a surprisingly subtle problem.</p><p>Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our <code>type</code> and <code>expanded-type</code> syntax classes so that we may thread a definition context around as we recursively expand:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now, we can add an additional case to <code>expanded-type</code> to handle <code>letrec-syntaxes+values</code>, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>But even this isn’t quite right. The problem with this implementation is that it throws away the existing <code>intdef-ctx</code> argument to <code>expanded-type</code>, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a <em>child</em> of the previous definition context by passing the old context as an argument to <code>syntax-local-make-definition-context</code>. This will ensure the parent bindings are brought into scope when expanding using the child context:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>With this in place, our example using <code>let-syntax</code> actually works!</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Bool))</span></code></pre><p>Pretty cool, isn’t it?</p><h3><a name="preserving-syntax-properties-and-source-locations"></a>Preserving syntax properties and source locations</h3><p>We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.</p><p>To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports <code>#%type:app</code> under a different name, like <code>#%type:apply</code>, we should expand to a piece of syntax that still has <code>#%type:apply</code> in application position instead of replacing it with <code>#%type:app</code>.</p><p>To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the <code>#%type:app</code> clause to the following:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span></code></pre><p>But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy <em>everything</em>, we can define two helper macros, <code>syntax/loc/props</code> and <code>quasisyntax/loc/props</code>, which are like <code>syntax/loc</code> and <code>quasisyntax/loc</code> but copy properties in addition to source location information:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span></code></pre><p>Using <code>syntax/loc/props</code>, we can be truly thorough about ensuring all properties are preserved:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span></code></pre><p>Applying this to the other relevant clauses, we get an updated version of the <code>expanded-type</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now we’re getting closer, but if you can believe it, even <em>this</em> isn’t good enough. The real expander’s implementation of <code>letrec-syntaxes+values</code> does two things our implementation does not: it copies properties and updates the <code>'origin</code> property to indicate the syntax came from a use of <code>letrec-syntaxes+values</code>, and it adds a <code>'disappeared-use</code> property to record the erased bindings for use by tools like DrRacket. We can apply <code>syntax-track-origin</code> and <code>internal-definition-context-track</code> to the resulting syntax to add the same properties the expander would:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span></code></pre><p>Now we’ve <em>finally</em> dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.</p><h3><a name="connecting-our-custom-language-to-hackett"></a>Connecting our custom language to Hackett</h3><p>It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-meta</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/parse</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/intdef</span> +<span class="w"> </span><span class="n">threading</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a <em>typechecker</em>. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.</p><p>But we don’t <em>just</em> need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom <code>#%app</code> binding that converts n-ary type applications to nested uses of <code>#%type:app</code>, as well as a nicer <code>forall</code> macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of <code>#%app</code>—one for types and one for values—since <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">Hackett supports multiple namespaces</a>, and each can have its own <code>#%app</code> binding.</p><p>The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom <code>syntax/parse</code> pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, <a href="https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt">it’s available on GitHub here</a>.</p><h2><a name="evaluation-limitations-and-acknowledgements"></a>Evaluation, limitations, and acknowledgements</h2><p>Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started <a href="https://twitter.com/lexi_lambda/status/976533916596097024">my new job</a> this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net <em>removal</em> of 100 lines from the rest of the codebase, which I consider an organizational win.</p><p>As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I <a href="https://twitter.com/lexi_lambda/status/985051504867446786">able to get it working</a> quickly and easily, I was able to do it in <a href="https://twitter.com/lexi_lambda/status/985052476473856000">no more than 20 lines of code</a>. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.</p><p>Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the <code>local-expand</code> stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes <code>syntax-parameterize</code>, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like <code>syntax-parameterize</code> cooperate better with things like Hackett’s type language.</p><p>Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> for <a href="https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf">his incredible work on constructing tools for the working macro developer</a>, including writing the fantastic <code>syntax/parse</code> library that powers essentially everything I do. Thank you both.</p><ol class="footnotes"></ol></article>A space of their own: adding a type namespace to Hackett2017-10-27T00:00:00Z2017-10-27T00:00:00ZAlexis King<article><p>As previously discussed on this blog, <a href="https://github.com/lexi-lambda/hackett">my programming language, Hackett</a>, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?</p><p>For now, at least, the answer is that Hackett will emulate Haskell: <strong>Hackett now has two namespaces</strong>. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.</p><h2><a name="why-two-namespaces"></a>Why two namespaces?</h2><p>Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.</p><p>This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="ss">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="ss">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="ss">c</span><span class="p">)))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly <em>annotate</em> when they wish to use a value from a different namespace:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">mapcar</span><span class="w"> </span><span class="nf">#&#39;</span><span class="nb">car</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="nv">c</span><span class="p">)))</span> +<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>The Common Lisp <code>#'x</code> reader abbreviation is equivalent to <code>(function x)</code>, and <code>function</code> is a special form that references a value in the function namespace.</p><p>While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.</p><p>However, Hackett is a little different from all of the aforementioned languages because Hackett has <em>types</em>. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is <em>always</em> syntactically unambiguous.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Even if types and values live in separate namespaces, there is no need for a <code>type</code> form a la CL’s <code>function</code> because it can always be determined implicitly.</p><p>For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span></code></pre><p>This defines a binding named <code>Tuple</code> at the type level, which is a <em>type constructor</em> of two arguments that produces a type of kind <code>*</code>,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> and another binding named <code>Tuple</code> at the value level, which is a <em>value constructor</em> of two arguments that produces a value of type <code>(Tuple a b)</code>.</p><p>But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor <code>tuple</code> instead of <code>Tuple</code>? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>This works fine. But what happens if the programmer decides to change the name of the <code>bar</code> value?</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">qux</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>Can you spot the bug? Disturbingly, this code <em>still compiles</em>! Even though <code>bar</code> is not a member of <code>Foo</code> anymore, it’s still a valid pattern, since names used as patterns match anything, just as the <code>y</code> pattern matches against any integer inside the <code>baz</code> constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: <code>(foo-&gt;integer (baz 42))</code> will still produce <code>0</code>, not <code>42</code>, since the first case always matches.</p><p>Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the <code>Tuple</code> example above, which is illegal if a programming language only supports a single namespace.</p><p>Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The <code>Tuple</code> example from above is perfectly legal Hackett.</p><h2><a name="adding-namespaces-to-a-language"></a>Adding namespaces to a language</h2><p>Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to <em>implement</em> such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?</p><p>Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: <strong>the remainder of this blog post is <em>highly technical</em>, and some of it involves some of the more esoteric corners of Racket’s macro system</strong>. This blog post is <em>not</em> representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.</p><h3><a name="namespaces-as-scopes"></a>Namespaces as scopes</h3><p>With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as <a href="https://www.cs.utah.edu/plt/scope-sets/"><em>sets of scopes</em></a>, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.</p><p>Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the <em>value scope</em>, and all identifiers in type position must have a different scope, which we will call the <em>type scope</em>. How do we create these scopes and apply them to identifiers? In Racket, we use a function called <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29"><code>make-syntax-introducer</code></a>, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can <em>add</em> the scope to all pieces of the syntax object, <em>remove</em> the scope, or <em>flip</em> the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call <code>make-syntax-introducer</code> once for each namespace:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>We define these in a <code>begin-for-syntax</code> block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Each of these two forms is like <code>begin</code>, which is a Racket form that is, for our purposes, essentially a no-op, but it applies <code>value-introducer</code> or <code>type-introducer</code> to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">value-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">type-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span></code></pre><p>This program produces the following output:</p><pre><code>'value-x +'type-x +</code></pre><p>It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.</p><p>However, although this is close, it isn’t <em>quite</em> right. What happens if we nest the two inside each other?</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span></code></pre><pre><code>x: identifier's binding is ambiguous + context...: + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) + #(190359 intdef) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) +</code></pre><p>Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of <code>x</code> is <em>ambiguous</em> because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of <code>begin/value</code> or <code>begin/type</code> to <em>override</em> outer ones, ensuring that a use can only be in a single namespace at a time.</p><p>To do this, we simply need to adjust <code>begin/value</code> and <code>begin/type</code> to remove the other scope in addition to adding the appropriate one:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Now our nested program runs, and it produces <code>'type-x</code>, which is exactly what we want—the “nearest” scope wins.</p><p>With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.</p><h3><a name="namespaces-that-cross-module-boundaries"></a>Namespaces that cross module boundaries</h3><p>The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.</p><p>Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the <em>other</em> module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export <em>symbols</em>, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named <code>foo</code>, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the <code>require</code> form’s job to attach the correct scopes to imported identifiers.</p><p>This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup> This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary <em>points</em> of having multiple namespaces.</p><p>What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s <code>require</code> and <code>provide</code> forms are extensible, it’s even possible to implement this mangling in a completely invisible way.</p><p>Currently, the scheme that Hackett uses is to prefix <code>#%hackett-type:</code> onto the beginning of any type exports. This can be defined in terms of a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29"><em>provide pre-transformer</em></a>, which is essentially a macro that cooperates with Racket’s <code>provide</code> form to control the export process. In this case, we can define our <code>type-out</code> provide pre-transformer in terms of <a href="https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29"><code>prefix-out</code></a>, a form built-in to Racket that allows prefixing the names of exports:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">type-out</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-provide-pre-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="w"> </span><span class="n">modes</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">pre-expand-export</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">prefix-out</span><span class="w"> </span><span class="n">#%hackett-type:</span> +<span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">type-introducer</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-out</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span> +<span class="w"> </span><span class="n">modes</span><span class="p">)]))))</span></code></pre><p>Note that we call <code>type-introducer</code> in this macro! That’s because we want to ensure that, when a user writes <code>(provide (type-out Foo))</code>, we look for <code>Foo</code> in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that <code>provide</code> knows <em>which</em> <code>Foo</code> is being provided.</p><p>Once we have referenced the correct binding, the use of <code>prefix-out</code> will appropriately add the <code>#%hackett-type:</code> prefix, so the exporting side is already done. Users do need to explicitly write <code>(type-out ....)</code> if they are exporting a particular type-level binding, but this is rarely necessary, since most users use <code>data</code> or <code>class</code> to export datatypes or typeclasses respectively, which can be modified to use <code>type-out</code> internally. Very little user code actually needs to change to support this adjustment.</p><p>Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the <code>#%hackett-type:</code> prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.</p><p>Just as we extended <code>provide</code> with a provide pre-transformer, we can extend <code>require</code> using a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29"><em>require transformer</em></a>. In code, this entire process looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">and~&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">regexp-match</span><span class="w"> </span><span class="sr">#rx"^#%hackett-type:(.+)$"</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="nb">second</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">unmangle-types-in</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-require-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">imports</span><span class="w"> </span><span class="n">sources</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">expand-import</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-in</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">match-lambda</span> +<span class="w"> </span><span class="p">[(</span><span class="k">and</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="n">local-id</span><span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">local-name</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol-&gt;string</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">local-id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">unmangled-type-name</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">local-name</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">unmangled-type-name</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">unmangled-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;symbol</span><span class="w"> </span><span class="n">unmangled-type-name</span><span class="p">)</span> +<span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="n">local-id</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">unmangled-id</span><span class="p">)</span> +<span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="n">i</span><span class="p">))])</span> +<span class="w"> </span><span class="n">imports</span><span class="p">)</span> +<span class="w"> </span><span class="n">sources</span><span class="p">)])))</span></code></pre><p>This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:</p><ol><li><p>It iterates over each import and calls <code>unmangle-type-name</code> on the imported symbol. If the result is <code>#f</code>, that means the import does not have the <code>#%hackett-type:</code> prefix, and it can be safely passed through unchanged.</p></li><li><p>If <code>unmangle-type-name</code> does <em>not</em> return <code>#f</code>, then it returns the unprefixed name, which is then provided to <code>datum-&gt;syntax</code>, which allows users to forge new identifiers in an <em>unhygienic</em> (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from <code>unmangle-type-name</code>, but with the lexical context of the original identifier.</p></li><li><p>Finally, we pass the new identifier to <code>type-introducer</code> to properly add the type scope, injecting the fresh binding into the type namespace.</p></li></ol><p>With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write <code>type-out</code> when exporting types, it is unlikely that users will want to write <code>unmangle-types-in</code> around each and every import in their program. For that reason, we can define a slightly modified version of <code>require</code> that implicitly wraps all of its subforms with <code>unmangle-types-in</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="p">(</span><span class="k">rename-out</span><span class="w"> </span><span class="p">[</span><span class="n">require/unmangle</span><span class="w"> </span><span class="k">require</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">require/unmangle</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-types-in</span><span class="w"> </span><span class="n">require-spec</span><span class="p">)</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>…and we’re done. Now, Hackett modules can properly import and export type-level bindings.</p><h3><a name="namespaces-plus-submodules-the-devil-s-in-the-details"></a>Namespaces plus submodules: the devil’s in the details</h3><p>Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of <em>submodules</em>.</p><p>Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.</p><p>Submodules in Racket come in two flavors: <code>module</code> and <code>module*</code>. The difference is what order, semantically, they are defined in. Submodules defined with <code>module</code> are essentially defined <em>before</em> their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with <code>module*</code> are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.</p><p>How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.</p><p>However, there is <a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29">a special sort of <code>module*</code> submodule that uses <code>#f</code> in place of a module language</a>, which gives a module access to <em>all</em> of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a <code>test</code> submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="c1">; not provided</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span> + +<span class="p">(</span><span class="k">module*</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">rackunit</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">check-equal?</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="mi">41</span><span class="p">)</span><span class="w"> </span><span class="mi">42</span><span class="p">))</span></code></pre><p>It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>Remember that <code>make-syntax-introducer</code> is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module <a href="https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29">instantiation</a>, as ensured by Racket’s <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">separate compilation guarantee</a>. This means that each module gets its <em>own</em> pair of scopes. This means the body of a <code>module*</code> submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.</p><p>Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we <em>can</em> preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29"><code>make-syntax-delta-introducer</code></a>, we can create a syntax introducer the adds or removes the <em>difference</em> between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for <code>value-introducer</code> and <code>type-introducer</code> to always operate on the same scopes on each module instantiation:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-value/type-introducers</span> +<span class="w"> </span><span class="n">value-introducer:id</span><span class="w"> </span><span class="n">type-introducer:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">scopeless-id</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">introducer-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">value-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">type-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">type-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-value/type-introducers</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="n">type-introducer</span><span class="p">)</span></code></pre><p>The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. <em>However</em>, code inside <code>begin-for-syntax</code> blocks is still re-evaluated every time the module is instantiated! This means we are <em>not</em> circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.</p><p>We still use <code>make-syntax-introducer</code> to create our two scopes, but critically, we only call <code>make-syntax-introducer</code> inside the <code>define-value/type-introducers</code> macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds <code>value-id</code> and <code>type-id</code> as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use <code>make-syntax-delta-introducer</code> to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.</p><p>By guaranteeing each namespace’s scope is always the same, even for different modules, <code>module*</code> submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.</p><h3><a name="the-final-stretch-making-scribble-documentation-namespace-aware"></a>The final stretch: making Scribble documentation namespace-aware</h3><p>As discussed in <a href="/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/">my previous blog post</a>, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.</p><p>In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!</p><p>Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.</p><p>To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with <code>t:</code> upon import, and they are bound to Scribble <a href="https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29"><em>element transformers</em></a> that indicate they should be typeset without the <code>t:</code> prefix. Fortunately, Scribble’s documentation forms <em>do</em> understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.</p><p>In practice, this means Hackett documentation must now include a proliferation of <code>t:</code> prefixes. For example, here is the code for a typeset REPL interaction:</p><pre><code class="pygments"><span class="n">@</span><span class="p">(</span><span class="n">hackett-examples</span> +<span class="w"> </span><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">square</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">t:-&gt;</span><span class="w"> </span><span class="n">t:Integer</span><span class="w"> </span><span class="n">t:Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">[[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="p">}])</span> +<span class="w"> </span><span class="p">(</span><span class="n">square</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span></code></pre><p>Note the use of <code>t:-&gt;</code> and <code>t:Integer</code> instead of <code>-&gt;</code> and <code>Integer</code>. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.</p><p>This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides <code>deftype</code> and <code>deftycon</code> forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to <code>t:</code>-prefixed identifiers to properly index documented forms. Similarly, <code>defdata</code> and <code>defclass</code> have been updated with an understanding of types.</p><p>The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of <code>defform</code> slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into <code>t:</code>-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.</p><h2><a name="analysis-and-unsolved-problems"></a>Analysis and unsolved problems</h2><p>When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).</p><p>For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom <code>require</code> form, types provided by a module’s <em>language</em> are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike <code>require</code>’s require transformers.</p><p>To circumvent this restriction, <code>#lang hackett</code>’s reader includes a somewhat ad-hoc solution that actually inserts a <code>require</code> into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.</p><p>That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do <em>any</em> special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully <a href="http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html">internalizes extra-linguistic mechanisms</a>.</p><p>As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:</p><ul><li><p><strong>Multi-parameter typeclasses</strong> are implemented, along with <strong>default typeclass method implementations</strong>.</p></li><li><p>Pattern-matching performs basic <strong>exhaustiveness checking</strong>, so unmatched cases are a compile-time error.</p></li><li><p>Hackett ships with a <strong>larger standard library</strong>, including an <code>Either</code> type and appropriate functions, an <code>Identity</code> type, a <code>MonadTrans</code> typeclass, and the <code>ReaderT</code> and <code>ErrorT</code> monad transformers.</p></li><li><p><strong>More things are documented</strong>, and parts of the documentation are slightly improved. Additionally, <strong>Hackett’s internals are much more heavily commented</strong>, hopefully making the project more accessible to new contributors.</p></li><li><p><strong>Parts of the typechecker are dramatically simplified</strong>, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.</p></li><li><p>As always, various bug fixes.</p></li></ul><p>Finally, special mention to two new contributors to Hackett, <a href="https://github.com/iitalics">Milo Turner</a> and <a href="https://github.com/Shamrock-Frost">Brendan Murphy</a>. Also special thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> and <a href="https://github.com/michaelballantyne">Michael Ballantyne</a> for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.</p><ol class="footnotes"><li id="footnote-1"><p>“But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have <code>#lang</code>. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate <code>#lang</code>, not a modified version of Hackett, so Hackett can optimize its user experience for what it <em>is</em>, not what it might be someday. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow <code>*</code> to be used to mean “type” at the kind level and “multiply” at the value level. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, <em>per phase</em>, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Hackett progress report: documentation, quality of life, and snake2017-08-28T00:00:00Z2017-08-28T00:00:00ZAlexis King<article><p>Three months ago, <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">I wrote a blog post describing my new, prototype implementation of my programming language, Hackett</a>. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/"><strong>the first (albeit quite incomplete) approach to Hackett’s documentation</strong></a>.</p><p>I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.</p><h2><a name="a-philosophy-of-documentation"></a>A philosophy of documentation</h2><p>Racket, as a project, has always had <a href="http://docs.racket-lang.org">wonderful documentation</a>. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably <a href="http://docs.racket-lang.org/scribble/index.html">Scribble, the Racket documentation tool</a>. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with <a href="http://docs.racket-lang.org/scribble/plt-manuals.html">a powerful library for documenting Racket code</a>. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.</p><p>Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an <code>@</code> character instead of <code>\</code>. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use <a href="http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29">the provided <code>defproc</code> form</a>:</p><pre><code class="pygments"><span class="n">@defproc</span><span class="p">[(</span><span class="nb">add1</span><span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="nb">number?</span><span class="p">])</span><span class="w"> </span><span class="nb">number?</span><span class="p">]{</span> +<span class="n">Returns</span><span class="w"> </span><span class="n">@racket</span><span class="p">[(</span><span class="nb">+</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="mi">1</span><span class="p">)]</span><span class="o">.</span><span class="p">}</span></code></pre><p>This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into <a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29">something like this</a>:</p><p><a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29"></a></p><p>The fact that Scribble documents are fully-fledged <em>programs</em> equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is <a href="http://docs.racket-lang.org/scribble/eval.html">the <code>scribble/example</code> module</a>, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special <code>eval:check</code> form that enables <a href="https://docs.python.org/3/library/doctest.html">doctest</a>-like checking, which allows documentation to serve double duty as a test suite.</p><p>Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is <em>designed</em> with the goal of supporting many different programming languages, and Scribble is no exception. Things like <a href="http://docs.racket-lang.org/scribble/eval.html"><code>scribble/example</code></a> essentially work out of the box with Hackett, and most of <a href="http://docs.racket-lang.org/scribble/plt-manuals.html"><code>scribble/manual</code></a> can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The <code>defproc</code> and <code>defstruct</code> forms are hardly builtins; they are defined as part of the <code>scribble/manual</code> library in terms of Scribble primitives, and <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt">we can do the same</a>.</p><p>Hackett’s documentation already defines three new forms, <code>defdata</code>, <code>defclass</code>, and <code>defmethod</code>, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the <code>Functor</code> typeclass using <code>defclass</code> like this:</p><pre><code class="pygments"><span class="n">@defclass</span><span class="p">[(</span><span class="n">Functor</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="nb">map</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="p">)})]]{</span> + +<span class="n">A</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">@deftech</span><span class="p">{</span><span class="n">functors</span><span class="p">}</span><span class="o">,</span><span class="w"> </span><span class="n">essentially</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="k">provide</span><span class="w"> </span><span class="n">a</span> +<span class="n">mapping</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">“piercing”</span><span class="w"> </span><span class="n">operation.</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">@racket</span><span class="p">[</span><span class="nb">map</span><span class="p">]</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">viewed</span><span class="w"> </span><span class="n">in</span> +<span class="n">different</span><span class="w"> </span><span class="n">ways:</span> + +<span class="k">...</span><span class="p">}</span></code></pre><p>With only a little more than the above code, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29">Hackett’s documentation includes a beautifully-typeset definition of the <code>Functor</code> typeclass</a>, including examples and rich prose:</p><p><a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29"></a></p><p>Scribble makes Hackett’s documentation shine.</p><h3><a name="a-tale-of-two-users"></a>A tale of two users</h3><p>For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.</p><p>A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.</p><p>However, for experienced users, an authoritative reference is <em>exactly</em> what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.</p><p>This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:</p><ul><li><p>Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.</p><p><a href="https://docs.oracle.com/javase/8/docs/api/">Java’s API documentation</a> documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly <em>small</em> language that does not often change.</p><p>On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java <em>does</em> provide guide-style documentation in the form of the <a href="https://docs.oracle.com/javase/tutorial/">Java Tutorials</a>, but they are of inconsistent quality.</p><p>More importantly, while the Java tutorials link to the API docs, the reverse is <strong>not</strong> true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.</p><p>Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.</p></li><li><p>Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.</p><p>The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is <a href="https://developer.mozilla.org/en-US/">MDN</a>. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.</p><p>The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.</p><p>This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. <a href="http://ramdajs.com/docs/">Ramda’s documentation is nothing but a reference</a>, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, <a href="http://passportjs.org/docs">Passport’s docs are essentially <em>only</em> a set of tutorials</a>, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method <em>does</em>. Fortunately, <a href="https://facebook.github.io/react/docs/hello-world.html">there are some libraries, like React</a>, that absolutely <em>nail</em> this, and they have both styles of documentation that are <strong>actually cross-referenced</strong>. Unfortunately, those are mostly the exceptions, not the norm.</p></li><li><p><a href="https://docs.python.org/3/index.html">Python’s documentation is interesting</a>, since it includes a set of tutorials alongside the API reference, and it <em>also</em> ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on <code>if</code> links to the section in the reference about <code>if</code>, but nothing goes in the other direction, which is something of a missed opportunity.</p></li><li><p><a href="https://hackage.haskell.org/package/base">Haskell manages to be especially bad here</a> (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.</p><p>It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and <a href="https://www.haskell.org/haddock/">Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look</a>.</p></li></ul><p>The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with <em>two</em> pieces of documentation: the <a href="http://docs.racket-lang.org/guide/index.html">Racket Guide</a> and the <a href="http://docs.racket-lang.org/reference/index.html">Racket Reference</a>. The guide includes over <strong>one hundred thousand</strong> words of explanations and examples, and the reference includes roughly <strong>half a million</strong>. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.</p><p>Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the <a href="http://docs.racket-lang.org/hackett/guide.html">Hackett Guide</a> and the <a href="http://docs.racket-lang.org/hackett/reference.html">Hackett Reference</a>, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily <em>need</em> to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a <em>lot</em> more accessible without making it any less useful for power users.</p><h2><a name="rounding-hackett-s-library-sanding-its-edges"></a>Rounding Hackett’s library, sanding its edges</h2><p>One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a <em>lot</em> of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.</p><p>Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:</p><ul><li><p>Hackett includes a <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29"><code>Double</code></a> type for working with IEEE 754 double-precision floating-point numbers.</p></li><li><p>Local definitions are supported via the <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29"><code>let</code></a> and <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29"><code>letrec</code></a> forms.</p></li><li><p>The prelude includes many more functions, especially <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29">functions on lists</a>.</p></li><li><p>The Hackett reader has been adjusted to support using <code>.</code> as a bare symbol, since <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29"><code>.</code> is the function composition operator</a>.</p></li><li><p>The Hackett REPL supports many more forms, including <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29">ADT</a>, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29">class</a>, and <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29">instance</a> definitions. Additionally, the REPL now uses <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a> instances to display the results of expressions. To compensate for the inability to print non-<a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a>able things, a new <code>(#:type expr)</code> syntax is permitted to print the type of <em>any</em> expression.</p></li><li><p>Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.</p></li></ul><p>Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189">heavily commented</a> with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.</p><p>One of the most exciting things about documenting Hackett has been realizing just <em>how much</em> already exists. Seriously, if you have gotten to this point in the blog post but haven’t read <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/">the actual documentation</a> yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for <em>me</em>: it’s a language I’d like to use.</p><p>Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!</p><h2><a name="a-demo-with-pictures"></a>A demo with pictures</h2><p>Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?</p><p>Hackett ships with a special package of demo libraries in the aptly-named <code>hackett-demo</code> package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">the previous Hackett blog post</a>, I demonstrated the capabilities of <code>hackett/demo/web-server</code>. In this blog post, we’re going to use <code>hackett/demo/pict</code> and <code>hackett/demo/pict/universe</code>, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!</p><p>As always, we’ll start with <code>#lang hackett</code>, and we’ll import the necessary libraries:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/pict</span> +<span class="w"> </span><span class="n">hackett/demo/pict/universe</span><span class="p">)</span></code></pre><p>With that, we can start immediately with a tiny example. Just to see how <code>hackett/demo/pict</code> works, let’s start by rendering a red square. We can do this by writing a <code>main</code> action that calls <code>print-pict</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))))</span></code></pre><p>If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!</p><p></p><p>Using the REPL, we can inspect the type of <code>print-pict</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">print-pict</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Unit</span><span class="p">))</span></code></pre><p>Unsurprisingly, displaying a picture to the screen needs <code>IO</code>. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of <code>filled-square</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">filled-square</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">Pict</span><span class="p">)</span></code></pre><p>No <code>IO</code> to be seen! This is because “picts” are entirely <em>pure</em> values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">{(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))</span> +<span class="w"> </span><span class="n">hc-append</span> +<span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))}))</span></code></pre><p>This code will print out a red square to the left of a blue one.</p><p></p><p>Again, <code>hc-append</code> is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">hc-append</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="n">Pict</span><span class="p">))</span></code></pre><p>Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!</p><h3><a name="implementing-a-snake-clone"></a>Implementing a snake clone</h3><p>This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the <code>pict</code> or <code>universe</code> libraries. Hopefully it’s still illustrative.</p><p>We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an <code>Eq</code> instance for our points.</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="n">d:left</span><span class="w"> </span><span class="n">d:right</span><span class="w"> </span><span class="n">d:up</span><span class="w"> </span><span class="n">d:down</span><span class="p">)</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">Eq</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="k">==</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)]</span><span class="w"> </span><span class="p">{{</span><span class="n">a</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">c</span><span class="p">}</span><span class="w"> </span><span class="n">&amp;&amp;</span><span class="w"> </span><span class="p">{</span><span class="n">b</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">d</span><span class="p">}})])</span></code></pre><p>With these two datatypes, we can implement a <code>move</code> function that accepts a point and a direction and produces a new point for an adjacent tile:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Direction</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:left</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:right</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:up</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">})]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:down</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">})])</span></code></pre><p>The next step is to define a type for our world state. The <code>big-bang</code> library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span> +<span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="c1">; snake direction</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; snake blocks</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; food blocks</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">set-ws-direction</span><span class="w"> </span><span class="p">[[</span><span class="n">d</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)])</span></code></pre><p>Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-width</span><span class="w"> </span><span class="mi">50</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-height</span><span class="w"> </span><span class="mi">30</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="p">{(</span><span class="n">d*</span><span class="w"> </span><span class="mf">15.0</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">integer-&gt;double</span><span class="p">})</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="p">(</span><span class="n">blank-rect</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-height</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">13.0</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="n">block</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="n">block</span><span class="p">))</span></code></pre><p>Now we can write our actual <code>render</code> function. To do this, we simply need to render each <code>Point</code> in our <code>World-State</code>’s two lists as a block on an <code>empty-board</code>. We’ll write a helper function, <code>render-on-board</code>, which does exactly that:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render-on-board</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Pict</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">pict</span><span class="w"> </span><span class="n">points</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">foldr</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">acc</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">pict</span><span class="p">))</span> +<span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="n">points</span><span class="p">)])</span></code></pre><p>This function uses <code>foldr</code> to collect each point and place the provided pict at the right location using <code>pin-over</code> on an empty board. Using <code>render-on-board</code>, we can write the <code>render</code> function in just a couple of lines:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)</span> +<span class="w"> </span><span class="mf">0.0</span><span class="w"> </span><span class="mf">0.0</span> +<span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="n">food-points</span><span class="p">))])</span></code></pre><p>Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the <code>big-bang</code> library provides a <code>random-integer</code> function, which we can use to write a <code>random-point</code> action:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">random-point</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">point</span><span class="w"> </span><span class="n">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span> +<span class="w"> </span><span class="n">&lt;*&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-height</span><span class="p">)})</span></code></pre><p>Hackett supports applicative notation using infix operators, so <code>random-point</code> looks remarkably readable. It also runs in <code>IO</code>, since the result is, obviously, random. Fortunately, the <code>on-tick</code> function runs in <code>IO</code> as well (unlike <code>render</code>, which must be completely pure), so we can use <code>random-point</code> when necessary to generate a new food block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">init!</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)})</span> +<span class="w"> </span><span class="p">{</span><span class="nb">reverse</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tail!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="nb">reverse</span><span class="p">})</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">new-snake-point</span><span class="w"> </span><span class="p">(</span><span class="n">move</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">(</span><span class="n">head!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">elem?</span><span class="w"> </span><span class="n">food-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">random-point</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">snake-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">delete</span><span class="w"> </span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">food-points</span><span class="p">)})))</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">init!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)}</span> +<span class="w"> </span><span class="n">food-points</span><span class="p">))))])</span></code></pre><p>This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to <code>new-snake-point</code>, then checks if there is a food block at that location. If there is, it generates a <code>new-food-point</code>, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.</p><p>The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our <code>set-ws-direction</code> function that we wrote earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-key</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">KeyEvent</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:left</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:left</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:right</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:right</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:up</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:up</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:down</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:down</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="k">_</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id</span><span class="p">}])</span></code></pre><p>The <code>on-key</code> function runs in <code>IO</code>, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in <code>pure</code>.</p><p>We’re almost done now—all we need to do is set up the <em>initial</em> state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">initial-state</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">initial-food</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">sequence</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="n">repeat</span><span class="w"> </span><span class="n">random-point</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d:right</span> +<span class="w"> </span><span class="p">{(</span><span class="n">point</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">24</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">23</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">nil</span><span class="p">}</span> +<span class="w"> </span><span class="n">initial-food</span><span class="p">))))</span></code></pre><p>Notably, we can use the <code>repeat</code> function to create an infinite list of <code>random-point</code> actions, <code>take</code> the first five of them, then call <code>sequence</code> to execute them from left to right. Now, all we have to do is put the pieces together in a <code>main</code> block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">state</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">initial-state</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">big-bang</span><span class="w"> </span><span class="n">state</span> +<span class="w"> </span><span class="kd">#:to-draw</span><span class="w"> </span><span class="n">render</span> +<span class="w"> </span><span class="kd">#:on-tick</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="mf">0.2</span> +<span class="w"> </span><span class="kd">#:on-key</span><span class="w"> </span><span class="n">on-key</span><span class="p">)))</span></code></pre><p>And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.</p><p></p><h2><a name="contributing-to-hackett"></a>Contributing to Hackett</h2><p>If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.</p><p>On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, <a href="https://github.com/lexi-lambda/hackett/issues">Hackett has an issue tracker</a>, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.</p><p>Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to <a href="https://github.com/lexi-lambda/hackett/issues">open an issue on GitHub</a>, send me a message on the <code>#racket</code> IRC channel on Freenode, or ping me on <a href="http://racket-slack.herokuapp.com">the Racket Slack team</a>.</p><h2><a name="acknowledgements"></a>Acknowledgements</h2><p>Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to <a href="https://github.com/gelisam">Samuel Gélineau, aka gelisam</a>, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.</p><p>Additionally, I want to specially thank <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.</p><p>Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.</p><ol class="footnotes"></ol></article>User-programmable infix operators in Racket2017-08-12T00:00:00Z2017-08-12T00:00:00ZAlexis King<article><p>Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in <a href="https://github.com/lexi-lambda/hackett">Hackett</a>, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.</p><p>Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done <em>without</em> modifying the stock <code>#lang racket</code> reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.</p><h2><a name="our-mission"></a>Our mission</h2><p>Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we <em>do</em> want:</p><ul><li><p>Infix operators should be user-extensible, not limited to a special set of built-in operators.</p></li><li><p>Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.</p></li><li><p>We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like <code>cons</code>. This allows <code>5 - 1 - 2</code> to be parsed as <code>(- (- 5 1) 2)</code>, but <code>5 :: 1 :: nil</code> to be parsed as <code>(:: 5 (:: 1 nil))</code>.</p></li></ul><p>These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:</p><ul><li><p>We will <strong>not</strong> permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing <code>(+ 1 2)</code> with <code>{1 + 2}</code>.</p></li><li><p>Our implementation will <strong>not</strong> support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.</p></li><li><p>All operators will be binary, and we will <strong>not</strong> support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.</p></li></ul><p>With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"infix.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> + +<span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">{</span><span class="mi">10</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="c1">; =&gt; &#39;(1 7)</span></code></pre><p>Let’s get started.</p><h2><a name="implementing-infix-operators"></a>Implementing infix operators</h2><p>Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:</p><ol><li><p>How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?</p></li><li><p>How can we associate fixity information with certain operators?</p></li></ol><p>We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.</p><p>So, how <em>do</em> we detect if a Racket expression is surrounded by curly braces? Normally, in <code>#lang racket</code>, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated <em>exactly</em> the same as parentheses:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.</p><p>Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29"><em>syntax objects</em></a>. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as <a href="http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29"><em>syntax properties</em></a>. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s <a href="http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29"><em>reader</em></a> (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named <a href="http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29"><code>'paren-shape</code></a>.</p><p>This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="no">#f</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\[</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\{</span></code></pre><p>This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.</p><h3><a name="customizing-application"></a>Customizing application</h3><p>Racket is a language <em>designed</em> to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named <a href="http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29"><code>#%app</code></a>, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>…into this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>What’s special about <code>#%app</code> is that the macroexpander will use whichever <code>#%app</code> is in scope in the expression’s lexical context, so if we write our own version of <code>#%app</code>, it will be used instead of the one from <code>#lang racket</code>. This is what we will use to hook into ordinary Racket expressions.</p><p>To write our custom version of <code>#%app</code>, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the <code>'paren-shape</code> syntax property, <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29"><code>syntax/parse/class/paren-shape</code></a>. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>This code will transform any applications surrounded in curly braces into one that starts with <code>#%infix</code> instead of <code>#%app</code>, so <code>{1 + 2}</code> will become <code>(#%infix 1 + 2)</code>, for example. The identifier <code>#%infix</code> isn’t actually special in any way, it just has a funny name, but we haven’t actually defined <code>#%infix</code> yet, so we need to do that next!</p><p>To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like <code>{1 + 2}</code>, should be converted into the equivalent prefix expressions, in this case <code>(+ 1 2)</code>. We can do this with a simple macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)])</span></code></pre><p>Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the <code>#%app</code> from <code>racket/base</code>, which will avoid any accidental infinite recursion between our <code>#%app</code> and <code>#%infix</code>. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>That’s pretty cool!</p><p>Of course, we probably want to support infix applications with more than just a single binary operator, such as <code>{1 + 2 + 3}</code>. We can implement that just by adding another case to <code>#%infix</code> that handles more subforms:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">10</span></code></pre><p>I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">5</span></code></pre><p>Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as <code>cons</code>, nested <code>-&gt;</code> types or contracts for curried functions, and <code>expt</code>, the exponentiation operator.</p><h3><a name="tracking-operator-fixity"></a>Tracking operator fixity</h3><p>Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.</p><p>Essentially, Racket (like Scheme) uses a <code>define-syntax</code> form to define macros, which is what <code>define-syntax-parser</code> eventually expands into. However, unlike Scheme, Racket’s <code>define-syntax</code> is not <em>just</em> for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Once a binding has been defined using <code>define-syntax</code>, a macro can look up the value associated with it by using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, which returns the compile-time value associated with an identifier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">foo</span><span class="p">)))</span> +<span class="c1">; =&gt; 3</span></code></pre><p>The cool thing is that <code>syntax-local-value</code> gets the value associated with a specific <em>binding</em>, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use <code>syntax-local-value</code> to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the <code>foo</code> binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because <code>foo</code> is not bound to anything at runtime.</p><p>To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the <a href="http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29"><code>prop:procedure</code></a> structure type property.</p><p>If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">))))</span></code></pre><p>This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for <code>runtime-binding</code>, but we can also include a value of our choosing for <code>fixity</code>. Here’s an example:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="nb">cons</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="p">))</span></code></pre><p>This new <code>::</code> binding will act, in every way, just like <code>cons</code>. If we use it in the REPL, you can see that it acts exactly the same:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></code></pre><p>However, we can also use <code>syntax-local-value</code> to extract this binding’s fixity at compile-time, and that’s what makes it interesting:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">::</span><span class="p">))))</span> +<span class="c1">; =&gt; &#39;right</span></code></pre><p>Using this extra compile-time information, we can adjust our <code>#%infix</code> macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used <code>syntax/parse/class/paren-shape</code> to make decisions based on the <code>'paren-shape</code> syntax property, we can use <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29"><code>syntax/parse/class/local-value</code></a> to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span></code></pre><p>Now, we can update <code>#%infix</code> to use our new <code>infix-op</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span></code></pre><p>Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via <code>#:when</code> clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of <code>#%infix</code>, we can successfully use <code>::</code> in an infix expression, and it will be parsed with the associativity that we expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Exciting!</p><h3><a name="a-nicer-interface-for-defining-infix-operators"></a>A nicer interface for defining infix operators</h3><p>We currently have to define infix operators by explicitly using <code>define-syntax</code>, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the <code>define-infix-operator</code> form from the example at the very beginning of this blog post.</p><p>Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the <code>define-syntax</code> definitions we were already writing:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span></code></pre><p>With this in hand, we can define some infix operators with a much nicer syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary <code>#lang racket</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">-1</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">256</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">64</span></code></pre><p>And you know what’s most amazing about this? The entire thing is <strong>only 50 lines of code</strong>. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/local-value</span> +<span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span> +<span class="w"> </span><span class="n">syntax/transformer</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>Racket is a hell of a programming language.</p><h2><a name="applications-limitations-and-implications"></a>Applications, limitations, and implications</h2><p>This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am <em>almost certain</em> that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even <em>locally</em>.</p><p>What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as <code>let-syntax</code> and <code>syntax-parameterize</code>. Using these tools, it would be entirely possible to implement a <code>with-fixity</code> macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make <code>/</code> right associative within a block of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="m">1/6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="nb">/</span><span class="w"> </span><span class="n">right</span><span class="p">])</span> +<span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span> +<span class="mi">1</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of <code>splicing-let</code> and <code>splicing-let-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span> +<span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="n">op:id</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}}]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">op-tmp</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">op</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let</span><span class="w"> </span><span class="p">([</span><span class="n">op-tmp</span><span class="w"> </span><span class="n">op</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">op-tmp</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span></code></pre><p>This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations <em>between</em> operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.</p><p>As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in <a href="https://github.com/lexi-lambda/hackett">Hackett</a> to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow <em>any</em> expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.</p><p>If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt">this file for the definition of infix bindings</a>, as well as <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101">this file for the defintion of infix application</a>. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like <code>{_ - 1}</code> to serve as a shorthand for <code>(lambda [x] {x - 1})</code>, but I haven’t yet decided if I like the tradeoffs involved.</p><p>It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in <code>#lang racket</code> without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.</p><p>Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to <code>#lang hackett</code>, there’s no reason something similar but more powerful couldn’t be built as a separate library by a <em>user</em> of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.</p><ol class="footnotes"></ol></article>Realizing Hackett, a metaprogrammable Haskell2017-05-27T00:00:00Z2017-05-27T00:00:00ZAlexis King<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questions2017-01-05T00:00:00Z2017-01-05T00:00:00ZAlexis King<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheses2017-01-02T00:00:00Z2017-01-02T00:00:00ZAlexis King<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/hackett.rss.xml b/feeds/hackett.rss.xml new file mode 100644 index 0000000..4d3593c --- /dev/null +++ b/feeds/hackett.rss.xml @@ -0,0 +1,650 @@ +Posts tagged ‘hackett’ | Alexis King’s BlogPosts tagged ‘hackett’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/hackett.html15 Apr 201815 Apr 201860Reimplementing Hackett’s type language: expanding to custom core forms in Rackethttps://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/https://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/15 Apr 2018<article><p>In the past couple of weeks, I <a href="https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935">completely rewrote the implementation of Hackett’s type language</a> to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.</p><p>This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">my previous blog post on Hackett</a>, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.</p><h2><a name="what-are-core-forms"></a>What are core forms?</h2><p>Before we can get started writing <em>custom core forms</em>, we need to understand the meaning of Racket’s plain old <em>core forms</em>. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.</p><p>To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a <code>#lang</code> line at the top. In this case, we’ll use <code>#lang racket</code> to keep things simple:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as <em>reading</em> the program. The <code>#lang</code> line controls how the program is read—some <code>#lang</code>s provide parsers that allow syntax that is very different from the parser used for <code>#lang racket</code>—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span></code></pre><p>Note the introduction of <code>#%module-begin</code>. Despite the fancy name, this is really just an ordinary macro provided by the <code>racket</code> language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with <code>#%module-begin</code>; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.</p><p>One the program has been read, it is subsequently <em>expanded</em> by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">2</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">call-with-values</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="n">add2</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">))</span> +<span class="w"> </span><span class="n">print-values</span><span class="p">)))</span></code></pre><p>Let’s note the things that changed:</p><ol><li><p><code>#%module-begin</code> was replaced with <code>#%plain-module-begin</code>. <code>#%plain-module-begin</code> is a binding that wraps the body of every expanded module, and all definitions of <code>#%module-begin</code> in any language must eventually expand to <code>#%plain-module-begin</code>. However, <code>#lang racket</code>’s <code>#%module-begin</code> doesn’t <em>just</em> expand to <code>#%plain-module-begin</code>, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints <code>5</code> even though there is no code related to printing in the original program!</p></li><li><p>The lambda shorthand used with <code>define</code> was converted to an explicit use of <code>lambda</code>, and it was expanded to <code>define-values</code>. In Racket, <code>define</code> and <code>define-syntax</code> are really just macros for <code>define-values</code> and <code>define-syntaxes</code> that only bind a single identifier.</p></li><li><p>All function applications were tagged explicitly with <code>#%plain-app</code>. This syntactically distinguishes function applications from uses of forms like <code>define-values</code> or <code>lambda</code>. It also allows languages to customize function application by providing their own macros named <code>#%app</code> (just like languages can provide their own macros named <code>#%module-begin</code> that expand to <code>#%plain-module-begin</code>), but that is outside the scope of this blog post.</p></li><li><p>All literals have been wrapped with <code>quote</code>, so <code>2</code> became <code>'2</code> and <code>3</code> became <code>'3</code>.</p></li></ol><p>Importantly, the resulting program contains <strong>no macros</strong>. Such programs are called <em>fully expanded</em>, since all macros have been eliminated and no further expansion can take place.</p><p>So what’s left behind? Well, some of the things in the program are literal data, like the numbers <code>2</code> and <code>3</code>. There are also some variable references, <code>x</code> and <code>add2</code>. Most of the program, however, is built out of primitives like <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, and <code>lambda</code>. These primitives are <em>core forms</em>—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.</p><p>In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:</p><pre><code class="pygments"><span class="n">var</span> <span class="n">add2</span> <span class="o">=</span> + <span class="n">function</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span> + +<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">add2</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span></code></pre><p>Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be <em>compiled</em>. The Racket compiler has built-in rules for how to compile core forms like <code>define-values</code>, <code>lambda</code>, and <code>#%plain-app</code>, and the result is optimized Racket bytecode.</p><p>In the remainder of this blog post, as most discussions of macros do, we’ll ignore the <em>read</em> and <em>compile</em> steps of the Racket program pipeline and focus exclusively on the <em>expand</em> step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.</p><h3><a name="racket-s-default-core-forms"></a>Racket’s default core forms</h3><p>So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, <code>lambda</code>, and <code>quote</code>, but there are many more. The full list is available in the section of the Racket reference named <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29">Fully Expanded Programs</a>, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like <code>define-syntaxes</code>, <code>if</code>, <code>let-values</code>, <code>letrec-values</code>, <code>begin</code>, <code>quote-syntax</code>, and <code>set!</code>. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.</p><p>These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, <code>#lang racket</code> implements <code>cond</code> as a macro that eventually expands into <code>if</code>, and it implements <code>syntax</code> as a macro that eventually expands into function calls and <code>quote-syntax</code>. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s <code>match</code> can expand into uses of <code>let</code> and <code>cond</code>, and it doesn’t need to concern itself with using <code>let-values</code> and <code>if</code>. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.</p><h3><a name="the-need-for-custom-core-forms"></a>The need for custom core forms</h3><p>With that in mind, why might we wish to define <em>custom</em> core forms? In fact, what would such a thing even mean? By their very nature, <em>all</em> Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.</p><p>Despite this, there <em>are</em> at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.</p><h4><a name="supporting-multiple-backends"></a>Supporting multiple backends</h4><p>The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket <code>#lang</code> that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might <em>also</em> wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.</p><p>In the case of a hardware description language, the custom core forms might include things like <code>input</code> and <code>output</code> for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could <em>additionally</em> define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.</p><p>Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.</p><h4><a name="compiling-an-extensible-embedded-language"></a>Compiling an extensible embedded language</h4><p>A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.</p><p>This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it <em>will</em> eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.</p><p>Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the <code>match</code> macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.</p><p>Existing DSLs of this type are rare, but they do exist. <code>syntax/parse</code> provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from <code>racket/match</code> to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While <code>match</code> is essentially just a traditional pattern-matcher, albeit an extensible one, <code>syntax-parse</code> is its own programming language, closer in some ways to Prolog than to Racket.</p><p>For this reason, <code>syntax/parse</code> has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as <a href="http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29"><em>pattern directives</em></a>. Here is an example of pattern directives in action, from my own <code>threading</code> library:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>Each directive is represented by a keyword, in this case <code>#:do</code> and <code>#:with</code>. Each directive has a corresponding keyword in the pattern language, in this case <code>~do</code> and <code>~parse</code>. Therefore, the above pattern could equivalently be written this way:</p><pre><code class="pygments"><span class="p">[{</span><span class="n">~and</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">~do</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using <code>#:declare</code>, so this is also equivalent:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">expr</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a <code>#:with</code> or <code>~parse</code> pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using <code>#:do</code> or <code>~do</code>, practical uses of <code>syntax/parse</code> really do involve quite a lot of programming in its pattern DSL.</p><p>But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—<code>#:declare</code>, <code>#:do</code>, and <code>#:with</code>, among others—are essentially the core forms of <code>syntax/parse</code>’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.</p><p>But what if <code>syntax/parse</code> could define its own core forms? What if, instead of <code>#:do</code>, <code>#:declare</code>, and <code>#:with</code> being implemented as keyword options specially recognized by the <code>syntax-parse</code> grammar, it defined <code>do</code>, <code>declare</code>, and <code>with</code> as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the <code>syntax/parse</code> core forms. The implementation of <code>syntax/parse</code> could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.</p><p>Now, to be fair, <code>syntax/parse</code> is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.</p><p>The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.</p><h2><a name="the-need-for-a-custom-type-language-in-hackett"></a>The need for a custom type language in Hackett</h2><p>In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for <em>both</em> of them:</p><ul><li><p>Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.</p></li><li><p>Hackett can <em>also</em> make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.</p></li></ul><p>The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:</p><ul><li><p><code>(#%type:con <em>id</em>)</code> — Type constructors, like <code>Integer</code> or <code>Maybe</code>. These are one of the fundamental building blocks of Hackett types.</p></li><li><p><code>(#%type:app <em>type</em> <em>type</em>)</code> — Type application, such as <code>(Maybe Integer)</code>. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of <code>#%type:app</code>.</p></li><li><p><code>(#%type:forall <em>id</em> <em>type</em>)</code> — Universal quantification. This is essentially a binding form, which binds any uses of <code>(#%type:bound-var <em>id</em>)</code> in <code><em>type</em></code>.</p></li><li><p><code>(#%type:qual <em>type</em> <em>type</em>)</code> — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like <code>Eq</code> are bound as type constructors.</p></li><li><p>Finally, Hackett types support three different varieties of type variables:</p><ul><li><p><code>(#%type:bound-var <em>id</em>)</code> — Bound type variables. These are only legal under a corresponding <code>#%type:forall</code>.</p></li><li><p><code>(#%type:wobbly-var <em>id</em>)</code> — Solver variables, which may unify with any other type as part of the typechecking process.</p></li><li><p><code>(#%type:rigid-var <em>id</em>)</code> — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.</p></li></ul></li></ul><p>To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we <em>can</em> bind them to macros that do nothing but raise an exception if something attempts to expand them:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span></code></pre><p>This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.</p><h3><a name="expanding-types-in-our-type-language"></a>Expanding types in our type language</h3><p>We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we <em>do</em> want to expand some of their subforms. For example, in the type <code>(#%type:app a b)</code>, we want to recursively expand <code>a</code> and <code>b</code>.</p><p>In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a>, and it has an option relevant to our needs: the stop list.</p><p>Often, <code>local-expand</code> is used to force the expander to completely, recursively expand a form. For example, by using <code>local-expand</code>, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="c1">; =&gt; (let-values ([(x) &#39;1]) (#%plain-app + x &#39;2))</span></code></pre><p>The third argument to <code>local-expand</code> is the <em>stop list</em>, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; =&gt; (#%type:forall x t)</span></code></pre><p>Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the <code>#%type:forall</code> identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding <code>t</code> since the expander has no way of knowing which pieces of <code>(#%type:forall x t)</code> it should expand! In this case, we want it to recur to expand <code>t</code>, since it should be a type, but not <code>x</code>, since <code>#%type:forall</code> essentially puts <code>x</code> in binding position.</p><p>Therefore, we have to get more clever. We need to call <code>local-expand</code> to produce a type, then we have to pattern-match on it and subsequently call <code>local-expand</code> <em>again</em> on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.</p><p>One good way to do this is to use <code>syntax/parse</code> syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using <code>local-expand</code> and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled <code>#%type:con</code> and <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:expanded-type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]))</span></code></pre><p>This blog post is definitely <em>not</em> a <code>syntax/parse</code> tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named <code>expansion</code>. This attribute contains the fully expanded version of the type currently being parsed. In the <code>#%type:con</code> case, <code>expansion</code> is just <code>this-syntax</code>, which holds the current piece of syntax being parsed. This makes sense, since uses of <code>#%type:con</code> just expand to themselves—expanding <code>(#%type:con Maybe)</code> should not perform any additional expansion on <code>Maybe</code>. This is one of Hackett’s atomic types.</p><p>In contrast, <code>#%type:app</code> <em>does</em> recursively expand its arguments. By annotating its two subforms with <code>:type</code>, the <code>type</code> syntax class will invoke <code>local-expand</code> on each subform, which will in turn use <code>expanded-type</code> to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once <code>a</code> and <code>b</code> have been expanded, <code>#%type:app</code> reassembles them into a new syntax object using <code>#'(#%type:app a.expansion b.expansion)</code>, which replaces their unexpanded versions with their new, expanded versions.</p><p>We can see this behavior by writing a small <code>expand-type</code> function that will expand its argument:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>Now we can use it to observe what happens when we try expanding a type using <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; #%type:app: expected type</span> +<span class="c1">; at: Maybe</span> +<span class="c1">; in: (#%type:app Maybe Integer)</span></code></pre><p>Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined <code>Maybe</code> or <code>Integer</code> anywhere. Let’s do so! We can define them as simple macros that expand into uses of <code>#%type:con</code>, which can be done easily using <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29"><code>make-variable-like-transformer</code></a> from <code>syntax/transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Maybe</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Integer</span><span class="p">)))</span></code></pre><p>Now, if we try expanding that same type again:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Integer))</span></code></pre><p>…it works! Neat. Now we just need to add the cases for the remaining forms in our type language:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">t:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like <code>let-syntax</code> to work in types. For example, we ought to be able to create a local type binding with <code>let-syntax</code> and have it just work. Unfortunately, it doesn’t:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; let-syntax: expected one of these identifiers: `#%type:con&#39;, `#%type:app&#39;, `#%type:forall&#39;, `#%type:qual&#39;, `#%type:bound-var&#39;, `#%type:wobbly-var&#39;, or `#%type:rigid-var&#39;</span> +<span class="c1">; at: letrec-syntaxes+values</span> +<span class="c1">; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))</span></code></pre><p>What went wrong? And why is it complaining about <code>letrec-syntaxes+values</code>? Well, if you read the documentation for <code>local-expand</code>, you’ll find that its behavior is a little more complicated than you might at first believe:</p><blockquote><p>If <em><code>stop-ids</code></em> is [a nonempty list containing more than just <code>module*</code>], then <code>begin</code>, <code>quote</code>, <code>set!</code>, <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, <code>if</code>, <code>begin0</code>, <code>with-continuation-mark</code>, <code>letrec-syntaxes+values</code>, <code>#%plain-app</code>, <code>#%expression</code>, <code>#%top</code>, and <code>#%variable-reference</code> are implicitly added to <em><code>stop-ids</code></em>. Expansion stops when the expander encounters any of the forms in <em><code>stop-ids</code></em>, and the result is the partially-expanded form.</p></blockquote><p>That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with <code>letrec-syntaxes+values</code> (which <code>let-syntax</code> expands to) is a reasonable one. If the expander naïvely expanded <code>letrec-syntaxes+values</code> in the presence of a nonempty stop list, it could cause some significant problems!</p><p>Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">))</span></code></pre><p>We see <code>let-syntax</code>, so we start by evaluating the expression on the right hand side of the <code>Bool</code> binding. This produces a transformer expression, so we bind <code>Bool</code> to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:</p><pre><code class="pygments"><span class="c1">; local bindings:</span> +<span class="c1">; Bool -&gt; #&lt;variable-like-transformer&gt;</span> +<span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)</span></code></pre><p>Now, the identifier in application position is <code>#%type:app</code>, and <code>#%type:app</code> is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the <code>let-syntax</code> needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is <code>(#%type:app Maybe Bool)</code>. But this is a problem, because when we then go to expand <code>Bool</code>, <code>Bool</code> isn’t in the local binding table anymore! The <code>let-syntax</code> was already erased, and <code>Bool</code> is unbound!</p><p>When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.</p><p>Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call <code>local-expand</code>. The bad news is that first-class definition contexts are <em>complicated</em>, and using them properly is a surprisingly subtle problem.</p><p>Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our <code>type</code> and <code>expanded-type</code> syntax classes so that we may thread a definition context around as we recursively expand:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now, we can add an additional case to <code>expanded-type</code> to handle <code>letrec-syntaxes+values</code>, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>But even this isn’t quite right. The problem with this implementation is that it throws away the existing <code>intdef-ctx</code> argument to <code>expanded-type</code>, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a <em>child</em> of the previous definition context by passing the old context as an argument to <code>syntax-local-make-definition-context</code>. This will ensure the parent bindings are brought into scope when expanding using the child context:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>With this in place, our example using <code>let-syntax</code> actually works!</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Bool))</span></code></pre><p>Pretty cool, isn’t it?</p><h3><a name="preserving-syntax-properties-and-source-locations"></a>Preserving syntax properties and source locations</h3><p>We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.</p><p>To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports <code>#%type:app</code> under a different name, like <code>#%type:apply</code>, we should expand to a piece of syntax that still has <code>#%type:apply</code> in application position instead of replacing it with <code>#%type:app</code>.</p><p>To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the <code>#%type:app</code> clause to the following:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span></code></pre><p>But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy <em>everything</em>, we can define two helper macros, <code>syntax/loc/props</code> and <code>quasisyntax/loc/props</code>, which are like <code>syntax/loc</code> and <code>quasisyntax/loc</code> but copy properties in addition to source location information:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span></code></pre><p>Using <code>syntax/loc/props</code>, we can be truly thorough about ensuring all properties are preserved:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span></code></pre><p>Applying this to the other relevant clauses, we get an updated version of the <code>expanded-type</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now we’re getting closer, but if you can believe it, even <em>this</em> isn’t good enough. The real expander’s implementation of <code>letrec-syntaxes+values</code> does two things our implementation does not: it copies properties and updates the <code>'origin</code> property to indicate the syntax came from a use of <code>letrec-syntaxes+values</code>, and it adds a <code>'disappeared-use</code> property to record the erased bindings for use by tools like DrRacket. We can apply <code>syntax-track-origin</code> and <code>internal-definition-context-track</code> to the resulting syntax to add the same properties the expander would:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span></code></pre><p>Now we’ve <em>finally</em> dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.</p><h3><a name="connecting-our-custom-language-to-hackett"></a>Connecting our custom language to Hackett</h3><p>It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-meta</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/parse</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/intdef</span> +<span class="w"> </span><span class="n">threading</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a <em>typechecker</em>. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.</p><p>But we don’t <em>just</em> need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom <code>#%app</code> binding that converts n-ary type applications to nested uses of <code>#%type:app</code>, as well as a nicer <code>forall</code> macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of <code>#%app</code>—one for types and one for values—since <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">Hackett supports multiple namespaces</a>, and each can have its own <code>#%app</code> binding.</p><p>The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom <code>syntax/parse</code> pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, <a href="https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt">it’s available on GitHub here</a>.</p><h2><a name="evaluation-limitations-and-acknowledgements"></a>Evaluation, limitations, and acknowledgements</h2><p>Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started <a href="https://twitter.com/lexi_lambda/status/976533916596097024">my new job</a> this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net <em>removal</em> of 100 lines from the rest of the codebase, which I consider an organizational win.</p><p>As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I <a href="https://twitter.com/lexi_lambda/status/985051504867446786">able to get it working</a> quickly and easily, I was able to do it in <a href="https://twitter.com/lexi_lambda/status/985052476473856000">no more than 20 lines of code</a>. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.</p><p>Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the <code>local-expand</code> stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes <code>syntax-parameterize</code>, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like <code>syntax-parameterize</code> cooperate better with things like Hackett’s type language.</p><p>Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> for <a href="https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf">his incredible work on constructing tools for the working macro developer</a>, including writing the fantastic <code>syntax/parse</code> library that powers essentially everything I do. Thank you both.</p><ol class="footnotes"></ol></article>A space of their own: adding a type namespace to Hacketthttps://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/https://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/27 Oct 2017<article><p>As previously discussed on this blog, <a href="https://github.com/lexi-lambda/hackett">my programming language, Hackett</a>, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?</p><p>For now, at least, the answer is that Hackett will emulate Haskell: <strong>Hackett now has two namespaces</strong>. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.</p><h2><a name="why-two-namespaces"></a>Why two namespaces?</h2><p>Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.</p><p>This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="ss">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="ss">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="ss">c</span><span class="p">)))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly <em>annotate</em> when they wish to use a value from a different namespace:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">mapcar</span><span class="w"> </span><span class="nf">#&#39;</span><span class="nb">car</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="nv">c</span><span class="p">)))</span> +<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>The Common Lisp <code>#'x</code> reader abbreviation is equivalent to <code>(function x)</code>, and <code>function</code> is a special form that references a value in the function namespace.</p><p>While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.</p><p>However, Hackett is a little different from all of the aforementioned languages because Hackett has <em>types</em>. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is <em>always</em> syntactically unambiguous.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Even if types and values live in separate namespaces, there is no need for a <code>type</code> form a la CL’s <code>function</code> because it can always be determined implicitly.</p><p>For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span></code></pre><p>This defines a binding named <code>Tuple</code> at the type level, which is a <em>type constructor</em> of two arguments that produces a type of kind <code>*</code>,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> and another binding named <code>Tuple</code> at the value level, which is a <em>value constructor</em> of two arguments that produces a value of type <code>(Tuple a b)</code>.</p><p>But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor <code>tuple</code> instead of <code>Tuple</code>? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>This works fine. But what happens if the programmer decides to change the name of the <code>bar</code> value?</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">qux</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>Can you spot the bug? Disturbingly, this code <em>still compiles</em>! Even though <code>bar</code> is not a member of <code>Foo</code> anymore, it’s still a valid pattern, since names used as patterns match anything, just as the <code>y</code> pattern matches against any integer inside the <code>baz</code> constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: <code>(foo-&gt;integer (baz 42))</code> will still produce <code>0</code>, not <code>42</code>, since the first case always matches.</p><p>Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the <code>Tuple</code> example above, which is illegal if a programming language only supports a single namespace.</p><p>Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The <code>Tuple</code> example from above is perfectly legal Hackett.</p><h2><a name="adding-namespaces-to-a-language"></a>Adding namespaces to a language</h2><p>Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to <em>implement</em> such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?</p><p>Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: <strong>the remainder of this blog post is <em>highly technical</em>, and some of it involves some of the more esoteric corners of Racket’s macro system</strong>. This blog post is <em>not</em> representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.</p><h3><a name="namespaces-as-scopes"></a>Namespaces as scopes</h3><p>With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as <a href="https://www.cs.utah.edu/plt/scope-sets/"><em>sets of scopes</em></a>, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.</p><p>Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the <em>value scope</em>, and all identifiers in type position must have a different scope, which we will call the <em>type scope</em>. How do we create these scopes and apply them to identifiers? In Racket, we use a function called <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29"><code>make-syntax-introducer</code></a>, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can <em>add</em> the scope to all pieces of the syntax object, <em>remove</em> the scope, or <em>flip</em> the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call <code>make-syntax-introducer</code> once for each namespace:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>We define these in a <code>begin-for-syntax</code> block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Each of these two forms is like <code>begin</code>, which is a Racket form that is, for our purposes, essentially a no-op, but it applies <code>value-introducer</code> or <code>type-introducer</code> to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">value-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">type-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span></code></pre><p>This program produces the following output:</p><pre><code>'value-x +'type-x +</code></pre><p>It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.</p><p>However, although this is close, it isn’t <em>quite</em> right. What happens if we nest the two inside each other?</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span></code></pre><pre><code>x: identifier's binding is ambiguous + context...: + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) + #(190359 intdef) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) +</code></pre><p>Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of <code>x</code> is <em>ambiguous</em> because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of <code>begin/value</code> or <code>begin/type</code> to <em>override</em> outer ones, ensuring that a use can only be in a single namespace at a time.</p><p>To do this, we simply need to adjust <code>begin/value</code> and <code>begin/type</code> to remove the other scope in addition to adding the appropriate one:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Now our nested program runs, and it produces <code>'type-x</code>, which is exactly what we want—the “nearest” scope wins.</p><p>With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.</p><h3><a name="namespaces-that-cross-module-boundaries"></a>Namespaces that cross module boundaries</h3><p>The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.</p><p>Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the <em>other</em> module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export <em>symbols</em>, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named <code>foo</code>, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the <code>require</code> form’s job to attach the correct scopes to imported identifiers.</p><p>This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup> This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary <em>points</em> of having multiple namespaces.</p><p>What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s <code>require</code> and <code>provide</code> forms are extensible, it’s even possible to implement this mangling in a completely invisible way.</p><p>Currently, the scheme that Hackett uses is to prefix <code>#%hackett-type:</code> onto the beginning of any type exports. This can be defined in terms of a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29"><em>provide pre-transformer</em></a>, which is essentially a macro that cooperates with Racket’s <code>provide</code> form to control the export process. In this case, we can define our <code>type-out</code> provide pre-transformer in terms of <a href="https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29"><code>prefix-out</code></a>, a form built-in to Racket that allows prefixing the names of exports:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">type-out</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-provide-pre-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="w"> </span><span class="n">modes</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">pre-expand-export</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">prefix-out</span><span class="w"> </span><span class="n">#%hackett-type:</span> +<span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">type-introducer</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-out</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span> +<span class="w"> </span><span class="n">modes</span><span class="p">)]))))</span></code></pre><p>Note that we call <code>type-introducer</code> in this macro! That’s because we want to ensure that, when a user writes <code>(provide (type-out Foo))</code>, we look for <code>Foo</code> in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that <code>provide</code> knows <em>which</em> <code>Foo</code> is being provided.</p><p>Once we have referenced the correct binding, the use of <code>prefix-out</code> will appropriately add the <code>#%hackett-type:</code> prefix, so the exporting side is already done. Users do need to explicitly write <code>(type-out ....)</code> if they are exporting a particular type-level binding, but this is rarely necessary, since most users use <code>data</code> or <code>class</code> to export datatypes or typeclasses respectively, which can be modified to use <code>type-out</code> internally. Very little user code actually needs to change to support this adjustment.</p><p>Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the <code>#%hackett-type:</code> prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.</p><p>Just as we extended <code>provide</code> with a provide pre-transformer, we can extend <code>require</code> using a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29"><em>require transformer</em></a>. In code, this entire process looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">and~&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">regexp-match</span><span class="w"> </span><span class="sr">#rx"^#%hackett-type:(.+)$"</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="nb">second</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">unmangle-types-in</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-require-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">imports</span><span class="w"> </span><span class="n">sources</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">expand-import</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-in</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">match-lambda</span> +<span class="w"> </span><span class="p">[(</span><span class="k">and</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="n">local-id</span><span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">local-name</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol-&gt;string</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">local-id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">unmangled-type-name</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">local-name</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">unmangled-type-name</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">unmangled-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;symbol</span><span class="w"> </span><span class="n">unmangled-type-name</span><span class="p">)</span> +<span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="n">local-id</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">unmangled-id</span><span class="p">)</span> +<span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="n">i</span><span class="p">))])</span> +<span class="w"> </span><span class="n">imports</span><span class="p">)</span> +<span class="w"> </span><span class="n">sources</span><span class="p">)])))</span></code></pre><p>This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:</p><ol><li><p>It iterates over each import and calls <code>unmangle-type-name</code> on the imported symbol. If the result is <code>#f</code>, that means the import does not have the <code>#%hackett-type:</code> prefix, and it can be safely passed through unchanged.</p></li><li><p>If <code>unmangle-type-name</code> does <em>not</em> return <code>#f</code>, then it returns the unprefixed name, which is then provided to <code>datum-&gt;syntax</code>, which allows users to forge new identifiers in an <em>unhygienic</em> (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from <code>unmangle-type-name</code>, but with the lexical context of the original identifier.</p></li><li><p>Finally, we pass the new identifier to <code>type-introducer</code> to properly add the type scope, injecting the fresh binding into the type namespace.</p></li></ol><p>With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write <code>type-out</code> when exporting types, it is unlikely that users will want to write <code>unmangle-types-in</code> around each and every import in their program. For that reason, we can define a slightly modified version of <code>require</code> that implicitly wraps all of its subforms with <code>unmangle-types-in</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="p">(</span><span class="k">rename-out</span><span class="w"> </span><span class="p">[</span><span class="n">require/unmangle</span><span class="w"> </span><span class="k">require</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">require/unmangle</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-types-in</span><span class="w"> </span><span class="n">require-spec</span><span class="p">)</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>…and we’re done. Now, Hackett modules can properly import and export type-level bindings.</p><h3><a name="namespaces-plus-submodules-the-devil-s-in-the-details"></a>Namespaces plus submodules: the devil’s in the details</h3><p>Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of <em>submodules</em>.</p><p>Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.</p><p>Submodules in Racket come in two flavors: <code>module</code> and <code>module*</code>. The difference is what order, semantically, they are defined in. Submodules defined with <code>module</code> are essentially defined <em>before</em> their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with <code>module*</code> are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.</p><p>How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.</p><p>However, there is <a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29">a special sort of <code>module*</code> submodule that uses <code>#f</code> in place of a module language</a>, which gives a module access to <em>all</em> of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a <code>test</code> submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="c1">; not provided</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span> + +<span class="p">(</span><span class="k">module*</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">rackunit</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">check-equal?</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="mi">41</span><span class="p">)</span><span class="w"> </span><span class="mi">42</span><span class="p">))</span></code></pre><p>It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>Remember that <code>make-syntax-introducer</code> is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module <a href="https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29">instantiation</a>, as ensured by Racket’s <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">separate compilation guarantee</a>. This means that each module gets its <em>own</em> pair of scopes. This means the body of a <code>module*</code> submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.</p><p>Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we <em>can</em> preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29"><code>make-syntax-delta-introducer</code></a>, we can create a syntax introducer the adds or removes the <em>difference</em> between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for <code>value-introducer</code> and <code>type-introducer</code> to always operate on the same scopes on each module instantiation:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-value/type-introducers</span> +<span class="w"> </span><span class="n">value-introducer:id</span><span class="w"> </span><span class="n">type-introducer:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">scopeless-id</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">introducer-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">value-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">type-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">type-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-value/type-introducers</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="n">type-introducer</span><span class="p">)</span></code></pre><p>The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. <em>However</em>, code inside <code>begin-for-syntax</code> blocks is still re-evaluated every time the module is instantiated! This means we are <em>not</em> circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.</p><p>We still use <code>make-syntax-introducer</code> to create our two scopes, but critically, we only call <code>make-syntax-introducer</code> inside the <code>define-value/type-introducers</code> macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds <code>value-id</code> and <code>type-id</code> as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use <code>make-syntax-delta-introducer</code> to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.</p><p>By guaranteeing each namespace’s scope is always the same, even for different modules, <code>module*</code> submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.</p><h3><a name="the-final-stretch-making-scribble-documentation-namespace-aware"></a>The final stretch: making Scribble documentation namespace-aware</h3><p>As discussed in <a href="/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/">my previous blog post</a>, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.</p><p>In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!</p><p>Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.</p><p>To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with <code>t:</code> upon import, and they are bound to Scribble <a href="https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29"><em>element transformers</em></a> that indicate they should be typeset without the <code>t:</code> prefix. Fortunately, Scribble’s documentation forms <em>do</em> understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.</p><p>In practice, this means Hackett documentation must now include a proliferation of <code>t:</code> prefixes. For example, here is the code for a typeset REPL interaction:</p><pre><code class="pygments"><span class="n">@</span><span class="p">(</span><span class="n">hackett-examples</span> +<span class="w"> </span><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">square</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">t:-&gt;</span><span class="w"> </span><span class="n">t:Integer</span><span class="w"> </span><span class="n">t:Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">[[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="p">}])</span> +<span class="w"> </span><span class="p">(</span><span class="n">square</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span></code></pre><p>Note the use of <code>t:-&gt;</code> and <code>t:Integer</code> instead of <code>-&gt;</code> and <code>Integer</code>. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.</p><p>This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides <code>deftype</code> and <code>deftycon</code> forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to <code>t:</code>-prefixed identifiers to properly index documented forms. Similarly, <code>defdata</code> and <code>defclass</code> have been updated with an understanding of types.</p><p>The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of <code>defform</code> slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into <code>t:</code>-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.</p><h2><a name="analysis-and-unsolved-problems"></a>Analysis and unsolved problems</h2><p>When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).</p><p>For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom <code>require</code> form, types provided by a module’s <em>language</em> are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike <code>require</code>’s require transformers.</p><p>To circumvent this restriction, <code>#lang hackett</code>’s reader includes a somewhat ad-hoc solution that actually inserts a <code>require</code> into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.</p><p>That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do <em>any</em> special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully <a href="http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html">internalizes extra-linguistic mechanisms</a>.</p><p>As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:</p><ul><li><p><strong>Multi-parameter typeclasses</strong> are implemented, along with <strong>default typeclass method implementations</strong>.</p></li><li><p>Pattern-matching performs basic <strong>exhaustiveness checking</strong>, so unmatched cases are a compile-time error.</p></li><li><p>Hackett ships with a <strong>larger standard library</strong>, including an <code>Either</code> type and appropriate functions, an <code>Identity</code> type, a <code>MonadTrans</code> typeclass, and the <code>ReaderT</code> and <code>ErrorT</code> monad transformers.</p></li><li><p><strong>More things are documented</strong>, and parts of the documentation are slightly improved. Additionally, <strong>Hackett’s internals are much more heavily commented</strong>, hopefully making the project more accessible to new contributors.</p></li><li><p><strong>Parts of the typechecker are dramatically simplified</strong>, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.</p></li><li><p>As always, various bug fixes.</p></li></ul><p>Finally, special mention to two new contributors to Hackett, <a href="https://github.com/iitalics">Milo Turner</a> and <a href="https://github.com/Shamrock-Frost">Brendan Murphy</a>. Also special thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> and <a href="https://github.com/michaelballantyne">Michael Ballantyne</a> for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.</p><ol class="footnotes"><li id="footnote-1"><p>“But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have <code>#lang</code>. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate <code>#lang</code>, not a modified version of Hackett, so Hackett can optimize its user experience for what it <em>is</em>, not what it might be someday. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow <code>*</code> to be used to mean “type” at the kind level and “multiply” at the value level. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, <em>per phase</em>, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Hackett progress report: documentation, quality of life, and snakehttps://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/https://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/28 Aug 2017<article><p>Three months ago, <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">I wrote a blog post describing my new, prototype implementation of my programming language, Hackett</a>. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/"><strong>the first (albeit quite incomplete) approach to Hackett’s documentation</strong></a>.</p><p>I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.</p><h2><a name="a-philosophy-of-documentation"></a>A philosophy of documentation</h2><p>Racket, as a project, has always had <a href="http://docs.racket-lang.org">wonderful documentation</a>. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably <a href="http://docs.racket-lang.org/scribble/index.html">Scribble, the Racket documentation tool</a>. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with <a href="http://docs.racket-lang.org/scribble/plt-manuals.html">a powerful library for documenting Racket code</a>. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.</p><p>Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an <code>@</code> character instead of <code>\</code>. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use <a href="http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29">the provided <code>defproc</code> form</a>:</p><pre><code class="pygments"><span class="n">@defproc</span><span class="p">[(</span><span class="nb">add1</span><span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="nb">number?</span><span class="p">])</span><span class="w"> </span><span class="nb">number?</span><span class="p">]{</span> +<span class="n">Returns</span><span class="w"> </span><span class="n">@racket</span><span class="p">[(</span><span class="nb">+</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="mi">1</span><span class="p">)]</span><span class="o">.</span><span class="p">}</span></code></pre><p>This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into <a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29">something like this</a>:</p><p><a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29"></a></p><p>The fact that Scribble documents are fully-fledged <em>programs</em> equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is <a href="http://docs.racket-lang.org/scribble/eval.html">the <code>scribble/example</code> module</a>, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special <code>eval:check</code> form that enables <a href="https://docs.python.org/3/library/doctest.html">doctest</a>-like checking, which allows documentation to serve double duty as a test suite.</p><p>Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is <em>designed</em> with the goal of supporting many different programming languages, and Scribble is no exception. Things like <a href="http://docs.racket-lang.org/scribble/eval.html"><code>scribble/example</code></a> essentially work out of the box with Hackett, and most of <a href="http://docs.racket-lang.org/scribble/plt-manuals.html"><code>scribble/manual</code></a> can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The <code>defproc</code> and <code>defstruct</code> forms are hardly builtins; they are defined as part of the <code>scribble/manual</code> library in terms of Scribble primitives, and <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt">we can do the same</a>.</p><p>Hackett’s documentation already defines three new forms, <code>defdata</code>, <code>defclass</code>, and <code>defmethod</code>, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the <code>Functor</code> typeclass using <code>defclass</code> like this:</p><pre><code class="pygments"><span class="n">@defclass</span><span class="p">[(</span><span class="n">Functor</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="nb">map</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="p">)})]]{</span> + +<span class="n">A</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">@deftech</span><span class="p">{</span><span class="n">functors</span><span class="p">}</span><span class="o">,</span><span class="w"> </span><span class="n">essentially</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="k">provide</span><span class="w"> </span><span class="n">a</span> +<span class="n">mapping</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">“piercing”</span><span class="w"> </span><span class="n">operation.</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">@racket</span><span class="p">[</span><span class="nb">map</span><span class="p">]</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">viewed</span><span class="w"> </span><span class="n">in</span> +<span class="n">different</span><span class="w"> </span><span class="n">ways:</span> + +<span class="k">...</span><span class="p">}</span></code></pre><p>With only a little more than the above code, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29">Hackett’s documentation includes a beautifully-typeset definition of the <code>Functor</code> typeclass</a>, including examples and rich prose:</p><p><a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29"></a></p><p>Scribble makes Hackett’s documentation shine.</p><h3><a name="a-tale-of-two-users"></a>A tale of two users</h3><p>For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.</p><p>A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.</p><p>However, for experienced users, an authoritative reference is <em>exactly</em> what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.</p><p>This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:</p><ul><li><p>Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.</p><p><a href="https://docs.oracle.com/javase/8/docs/api/">Java’s API documentation</a> documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly <em>small</em> language that does not often change.</p><p>On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java <em>does</em> provide guide-style documentation in the form of the <a href="https://docs.oracle.com/javase/tutorial/">Java Tutorials</a>, but they are of inconsistent quality.</p><p>More importantly, while the Java tutorials link to the API docs, the reverse is <strong>not</strong> true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.</p><p>Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.</p></li><li><p>Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.</p><p>The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is <a href="https://developer.mozilla.org/en-US/">MDN</a>. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.</p><p>The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.</p><p>This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. <a href="http://ramdajs.com/docs/">Ramda’s documentation is nothing but a reference</a>, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, <a href="http://passportjs.org/docs">Passport’s docs are essentially <em>only</em> a set of tutorials</a>, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method <em>does</em>. Fortunately, <a href="https://facebook.github.io/react/docs/hello-world.html">there are some libraries, like React</a>, that absolutely <em>nail</em> this, and they have both styles of documentation that are <strong>actually cross-referenced</strong>. Unfortunately, those are mostly the exceptions, not the norm.</p></li><li><p><a href="https://docs.python.org/3/index.html">Python’s documentation is interesting</a>, since it includes a set of tutorials alongside the API reference, and it <em>also</em> ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on <code>if</code> links to the section in the reference about <code>if</code>, but nothing goes in the other direction, which is something of a missed opportunity.</p></li><li><p><a href="https://hackage.haskell.org/package/base">Haskell manages to be especially bad here</a> (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.</p><p>It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and <a href="https://www.haskell.org/haddock/">Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look</a>.</p></li></ul><p>The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with <em>two</em> pieces of documentation: the <a href="http://docs.racket-lang.org/guide/index.html">Racket Guide</a> and the <a href="http://docs.racket-lang.org/reference/index.html">Racket Reference</a>. The guide includes over <strong>one hundred thousand</strong> words of explanations and examples, and the reference includes roughly <strong>half a million</strong>. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.</p><p>Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the <a href="http://docs.racket-lang.org/hackett/guide.html">Hackett Guide</a> and the <a href="http://docs.racket-lang.org/hackett/reference.html">Hackett Reference</a>, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily <em>need</em> to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a <em>lot</em> more accessible without making it any less useful for power users.</p><h2><a name="rounding-hackett-s-library-sanding-its-edges"></a>Rounding Hackett’s library, sanding its edges</h2><p>One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a <em>lot</em> of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.</p><p>Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:</p><ul><li><p>Hackett includes a <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29"><code>Double</code></a> type for working with IEEE 754 double-precision floating-point numbers.</p></li><li><p>Local definitions are supported via the <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29"><code>let</code></a> and <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29"><code>letrec</code></a> forms.</p></li><li><p>The prelude includes many more functions, especially <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29">functions on lists</a>.</p></li><li><p>The Hackett reader has been adjusted to support using <code>.</code> as a bare symbol, since <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29"><code>.</code> is the function composition operator</a>.</p></li><li><p>The Hackett REPL supports many more forms, including <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29">ADT</a>, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29">class</a>, and <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29">instance</a> definitions. Additionally, the REPL now uses <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a> instances to display the results of expressions. To compensate for the inability to print non-<a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a>able things, a new <code>(#:type expr)</code> syntax is permitted to print the type of <em>any</em> expression.</p></li><li><p>Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.</p></li></ul><p>Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189">heavily commented</a> with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.</p><p>One of the most exciting things about documenting Hackett has been realizing just <em>how much</em> already exists. Seriously, if you have gotten to this point in the blog post but haven’t read <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/">the actual documentation</a> yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for <em>me</em>: it’s a language I’d like to use.</p><p>Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!</p><h2><a name="a-demo-with-pictures"></a>A demo with pictures</h2><p>Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?</p><p>Hackett ships with a special package of demo libraries in the aptly-named <code>hackett-demo</code> package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">the previous Hackett blog post</a>, I demonstrated the capabilities of <code>hackett/demo/web-server</code>. In this blog post, we’re going to use <code>hackett/demo/pict</code> and <code>hackett/demo/pict/universe</code>, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!</p><p>As always, we’ll start with <code>#lang hackett</code>, and we’ll import the necessary libraries:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/pict</span> +<span class="w"> </span><span class="n">hackett/demo/pict/universe</span><span class="p">)</span></code></pre><p>With that, we can start immediately with a tiny example. Just to see how <code>hackett/demo/pict</code> works, let’s start by rendering a red square. We can do this by writing a <code>main</code> action that calls <code>print-pict</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))))</span></code></pre><p>If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!</p><p></p><p>Using the REPL, we can inspect the type of <code>print-pict</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">print-pict</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Unit</span><span class="p">))</span></code></pre><p>Unsurprisingly, displaying a picture to the screen needs <code>IO</code>. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of <code>filled-square</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">filled-square</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">Pict</span><span class="p">)</span></code></pre><p>No <code>IO</code> to be seen! This is because “picts” are entirely <em>pure</em> values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">{(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))</span> +<span class="w"> </span><span class="n">hc-append</span> +<span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))}))</span></code></pre><p>This code will print out a red square to the left of a blue one.</p><p></p><p>Again, <code>hc-append</code> is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">hc-append</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="n">Pict</span><span class="p">))</span></code></pre><p>Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!</p><h3><a name="implementing-a-snake-clone"></a>Implementing a snake clone</h3><p>This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the <code>pict</code> or <code>universe</code> libraries. Hopefully it’s still illustrative.</p><p>We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an <code>Eq</code> instance for our points.</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="n">d:left</span><span class="w"> </span><span class="n">d:right</span><span class="w"> </span><span class="n">d:up</span><span class="w"> </span><span class="n">d:down</span><span class="p">)</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">Eq</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="k">==</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)]</span><span class="w"> </span><span class="p">{{</span><span class="n">a</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">c</span><span class="p">}</span><span class="w"> </span><span class="n">&amp;&amp;</span><span class="w"> </span><span class="p">{</span><span class="n">b</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">d</span><span class="p">}})])</span></code></pre><p>With these two datatypes, we can implement a <code>move</code> function that accepts a point and a direction and produces a new point for an adjacent tile:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Direction</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:left</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:right</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:up</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">})]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:down</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">})])</span></code></pre><p>The next step is to define a type for our world state. The <code>big-bang</code> library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span> +<span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="c1">; snake direction</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; snake blocks</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; food blocks</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">set-ws-direction</span><span class="w"> </span><span class="p">[[</span><span class="n">d</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)])</span></code></pre><p>Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-width</span><span class="w"> </span><span class="mi">50</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-height</span><span class="w"> </span><span class="mi">30</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="p">{(</span><span class="n">d*</span><span class="w"> </span><span class="mf">15.0</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">integer-&gt;double</span><span class="p">})</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="p">(</span><span class="n">blank-rect</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-height</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">13.0</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="n">block</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="n">block</span><span class="p">))</span></code></pre><p>Now we can write our actual <code>render</code> function. To do this, we simply need to render each <code>Point</code> in our <code>World-State</code>’s two lists as a block on an <code>empty-board</code>. We’ll write a helper function, <code>render-on-board</code>, which does exactly that:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render-on-board</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Pict</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">pict</span><span class="w"> </span><span class="n">points</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">foldr</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">acc</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">pict</span><span class="p">))</span> +<span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="n">points</span><span class="p">)])</span></code></pre><p>This function uses <code>foldr</code> to collect each point and place the provided pict at the right location using <code>pin-over</code> on an empty board. Using <code>render-on-board</code>, we can write the <code>render</code> function in just a couple of lines:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)</span> +<span class="w"> </span><span class="mf">0.0</span><span class="w"> </span><span class="mf">0.0</span> +<span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="n">food-points</span><span class="p">))])</span></code></pre><p>Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the <code>big-bang</code> library provides a <code>random-integer</code> function, which we can use to write a <code>random-point</code> action:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">random-point</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">point</span><span class="w"> </span><span class="n">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span> +<span class="w"> </span><span class="n">&lt;*&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-height</span><span class="p">)})</span></code></pre><p>Hackett supports applicative notation using infix operators, so <code>random-point</code> looks remarkably readable. It also runs in <code>IO</code>, since the result is, obviously, random. Fortunately, the <code>on-tick</code> function runs in <code>IO</code> as well (unlike <code>render</code>, which must be completely pure), so we can use <code>random-point</code> when necessary to generate a new food block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">init!</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)})</span> +<span class="w"> </span><span class="p">{</span><span class="nb">reverse</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tail!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="nb">reverse</span><span class="p">})</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">new-snake-point</span><span class="w"> </span><span class="p">(</span><span class="n">move</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">(</span><span class="n">head!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">elem?</span><span class="w"> </span><span class="n">food-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">random-point</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">snake-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">delete</span><span class="w"> </span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">food-points</span><span class="p">)})))</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">init!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)}</span> +<span class="w"> </span><span class="n">food-points</span><span class="p">))))])</span></code></pre><p>This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to <code>new-snake-point</code>, then checks if there is a food block at that location. If there is, it generates a <code>new-food-point</code>, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.</p><p>The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our <code>set-ws-direction</code> function that we wrote earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-key</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">KeyEvent</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:left</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:left</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:right</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:right</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:up</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:up</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:down</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:down</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="k">_</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id</span><span class="p">}])</span></code></pre><p>The <code>on-key</code> function runs in <code>IO</code>, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in <code>pure</code>.</p><p>We’re almost done now—all we need to do is set up the <em>initial</em> state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">initial-state</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">initial-food</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">sequence</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="n">repeat</span><span class="w"> </span><span class="n">random-point</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d:right</span> +<span class="w"> </span><span class="p">{(</span><span class="n">point</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">24</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">23</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">nil</span><span class="p">}</span> +<span class="w"> </span><span class="n">initial-food</span><span class="p">))))</span></code></pre><p>Notably, we can use the <code>repeat</code> function to create an infinite list of <code>random-point</code> actions, <code>take</code> the first five of them, then call <code>sequence</code> to execute them from left to right. Now, all we have to do is put the pieces together in a <code>main</code> block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">state</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">initial-state</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">big-bang</span><span class="w"> </span><span class="n">state</span> +<span class="w"> </span><span class="kd">#:to-draw</span><span class="w"> </span><span class="n">render</span> +<span class="w"> </span><span class="kd">#:on-tick</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="mf">0.2</span> +<span class="w"> </span><span class="kd">#:on-key</span><span class="w"> </span><span class="n">on-key</span><span class="p">)))</span></code></pre><p>And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.</p><p></p><h2><a name="contributing-to-hackett"></a>Contributing to Hackett</h2><p>If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.</p><p>On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, <a href="https://github.com/lexi-lambda/hackett/issues">Hackett has an issue tracker</a>, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.</p><p>Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to <a href="https://github.com/lexi-lambda/hackett/issues">open an issue on GitHub</a>, send me a message on the <code>#racket</code> IRC channel on Freenode, or ping me on <a href="http://racket-slack.herokuapp.com">the Racket Slack team</a>.</p><h2><a name="acknowledgements"></a>Acknowledgements</h2><p>Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to <a href="https://github.com/gelisam">Samuel Gélineau, aka gelisam</a>, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.</p><p>Additionally, I want to specially thank <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.</p><p>Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.</p><ol class="footnotes"></ol></article>User-programmable infix operators in Rackethttps://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/https://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/12 Aug 2017<article><p>Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in <a href="https://github.com/lexi-lambda/hackett">Hackett</a>, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.</p><p>Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done <em>without</em> modifying the stock <code>#lang racket</code> reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.</p><h2><a name="our-mission"></a>Our mission</h2><p>Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we <em>do</em> want:</p><ul><li><p>Infix operators should be user-extensible, not limited to a special set of built-in operators.</p></li><li><p>Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.</p></li><li><p>We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like <code>cons</code>. This allows <code>5 - 1 - 2</code> to be parsed as <code>(- (- 5 1) 2)</code>, but <code>5 :: 1 :: nil</code> to be parsed as <code>(:: 5 (:: 1 nil))</code>.</p></li></ul><p>These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:</p><ul><li><p>We will <strong>not</strong> permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing <code>(+ 1 2)</code> with <code>{1 + 2}</code>.</p></li><li><p>Our implementation will <strong>not</strong> support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.</p></li><li><p>All operators will be binary, and we will <strong>not</strong> support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.</p></li></ul><p>With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"infix.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> + +<span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">{</span><span class="mi">10</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="c1">; =&gt; &#39;(1 7)</span></code></pre><p>Let’s get started.</p><h2><a name="implementing-infix-operators"></a>Implementing infix operators</h2><p>Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:</p><ol><li><p>How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?</p></li><li><p>How can we associate fixity information with certain operators?</p></li></ol><p>We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.</p><p>So, how <em>do</em> we detect if a Racket expression is surrounded by curly braces? Normally, in <code>#lang racket</code>, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated <em>exactly</em> the same as parentheses:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.</p><p>Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29"><em>syntax objects</em></a>. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as <a href="http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29"><em>syntax properties</em></a>. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s <a href="http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29"><em>reader</em></a> (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named <a href="http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29"><code>'paren-shape</code></a>.</p><p>This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="no">#f</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\[</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\{</span></code></pre><p>This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.</p><h3><a name="customizing-application"></a>Customizing application</h3><p>Racket is a language <em>designed</em> to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named <a href="http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29"><code>#%app</code></a>, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>…into this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>What’s special about <code>#%app</code> is that the macroexpander will use whichever <code>#%app</code> is in scope in the expression’s lexical context, so if we write our own version of <code>#%app</code>, it will be used instead of the one from <code>#lang racket</code>. This is what we will use to hook into ordinary Racket expressions.</p><p>To write our custom version of <code>#%app</code>, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the <code>'paren-shape</code> syntax property, <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29"><code>syntax/parse/class/paren-shape</code></a>. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>This code will transform any applications surrounded in curly braces into one that starts with <code>#%infix</code> instead of <code>#%app</code>, so <code>{1 + 2}</code> will become <code>(#%infix 1 + 2)</code>, for example. The identifier <code>#%infix</code> isn’t actually special in any way, it just has a funny name, but we haven’t actually defined <code>#%infix</code> yet, so we need to do that next!</p><p>To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like <code>{1 + 2}</code>, should be converted into the equivalent prefix expressions, in this case <code>(+ 1 2)</code>. We can do this with a simple macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)])</span></code></pre><p>Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the <code>#%app</code> from <code>racket/base</code>, which will avoid any accidental infinite recursion between our <code>#%app</code> and <code>#%infix</code>. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>That’s pretty cool!</p><p>Of course, we probably want to support infix applications with more than just a single binary operator, such as <code>{1 + 2 + 3}</code>. We can implement that just by adding another case to <code>#%infix</code> that handles more subforms:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">10</span></code></pre><p>I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">5</span></code></pre><p>Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as <code>cons</code>, nested <code>-&gt;</code> types or contracts for curried functions, and <code>expt</code>, the exponentiation operator.</p><h3><a name="tracking-operator-fixity"></a>Tracking operator fixity</h3><p>Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.</p><p>Essentially, Racket (like Scheme) uses a <code>define-syntax</code> form to define macros, which is what <code>define-syntax-parser</code> eventually expands into. However, unlike Scheme, Racket’s <code>define-syntax</code> is not <em>just</em> for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Once a binding has been defined using <code>define-syntax</code>, a macro can look up the value associated with it by using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, which returns the compile-time value associated with an identifier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">foo</span><span class="p">)))</span> +<span class="c1">; =&gt; 3</span></code></pre><p>The cool thing is that <code>syntax-local-value</code> gets the value associated with a specific <em>binding</em>, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use <code>syntax-local-value</code> to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the <code>foo</code> binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because <code>foo</code> is not bound to anything at runtime.</p><p>To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the <a href="http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29"><code>prop:procedure</code></a> structure type property.</p><p>If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">))))</span></code></pre><p>This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for <code>runtime-binding</code>, but we can also include a value of our choosing for <code>fixity</code>. Here’s an example:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="nb">cons</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="p">))</span></code></pre><p>This new <code>::</code> binding will act, in every way, just like <code>cons</code>. If we use it in the REPL, you can see that it acts exactly the same:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></code></pre><p>However, we can also use <code>syntax-local-value</code> to extract this binding’s fixity at compile-time, and that’s what makes it interesting:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">::</span><span class="p">))))</span> +<span class="c1">; =&gt; &#39;right</span></code></pre><p>Using this extra compile-time information, we can adjust our <code>#%infix</code> macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used <code>syntax/parse/class/paren-shape</code> to make decisions based on the <code>'paren-shape</code> syntax property, we can use <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29"><code>syntax/parse/class/local-value</code></a> to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span></code></pre><p>Now, we can update <code>#%infix</code> to use our new <code>infix-op</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span></code></pre><p>Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via <code>#:when</code> clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of <code>#%infix</code>, we can successfully use <code>::</code> in an infix expression, and it will be parsed with the associativity that we expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Exciting!</p><h3><a name="a-nicer-interface-for-defining-infix-operators"></a>A nicer interface for defining infix operators</h3><p>We currently have to define infix operators by explicitly using <code>define-syntax</code>, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the <code>define-infix-operator</code> form from the example at the very beginning of this blog post.</p><p>Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the <code>define-syntax</code> definitions we were already writing:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span></code></pre><p>With this in hand, we can define some infix operators with a much nicer syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary <code>#lang racket</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">-1</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">256</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">64</span></code></pre><p>And you know what’s most amazing about this? The entire thing is <strong>only 50 lines of code</strong>. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/local-value</span> +<span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span> +<span class="w"> </span><span class="n">syntax/transformer</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>Racket is a hell of a programming language.</p><h2><a name="applications-limitations-and-implications"></a>Applications, limitations, and implications</h2><p>This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am <em>almost certain</em> that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even <em>locally</em>.</p><p>What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as <code>let-syntax</code> and <code>syntax-parameterize</code>. Using these tools, it would be entirely possible to implement a <code>with-fixity</code> macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make <code>/</code> right associative within a block of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="m">1/6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="nb">/</span><span class="w"> </span><span class="n">right</span><span class="p">])</span> +<span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span> +<span class="mi">1</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of <code>splicing-let</code> and <code>splicing-let-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span> +<span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="n">op:id</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}}]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">op-tmp</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">op</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let</span><span class="w"> </span><span class="p">([</span><span class="n">op-tmp</span><span class="w"> </span><span class="n">op</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">op-tmp</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span></code></pre><p>This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations <em>between</em> operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.</p><p>As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in <a href="https://github.com/lexi-lambda/hackett">Hackett</a> to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow <em>any</em> expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.</p><p>If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt">this file for the definition of infix bindings</a>, as well as <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101">this file for the defintion of infix application</a>. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like <code>{_ - 1}</code> to serve as a shorthand for <code>(lambda [x] {x - 1})</code>, but I haven’t yet decided if I like the tradeoffs involved.</p><p>It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in <code>#lang racket</code> without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.</p><p>Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to <code>#lang hackett</code>, there’s no reason something similar but more powerful couldn’t be built as a separate library by a <em>user</em> of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.</p><ol class="footnotes"></ol></article>Realizing Hackett, a metaprogrammable Haskellhttps://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/https://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/27 May 2017<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questionshttps://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/https://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/05 Jan 2017<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheseshttps://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/https://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/02 Jan 2017<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/haskell.atom.xml b/feeds/haskell.atom.xml new file mode 100644 index 0000000..51cc3a1 --- /dev/null +++ b/feeds/haskell.atom.xml @@ -0,0 +1,1510 @@ +Posts tagged ‘haskell’ | Alexis King’s Blog2021-03-25T00:00:00ZAn introduction to typeclass metaprogramming2021-03-25T00:00:00Z2021-03-25T00:00:00ZAlexis King<article><p><em>Typeclass metaprogramming</em> is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem), and it is the core mechanism used to implement generic programming via <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">GHC generics</a>. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.</p><p>This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does <em>not</em> attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also <em>not</em> a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.</p><h2><a name="part-1-basic-building-blocks"></a>Part 1: Basic building blocks</h2><p>Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.</p><p>To start, we’ll cover the absolute foundations of typeclass metaprogramming.</p><h3><a name="typeclasses-as-functions-from-types-to-terms"></a>Typeclasses as functions from types to terms</h3><p>As its name implies, typeclass metaprogramming (henceforth TMP<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup>) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic <code>==</code> operator via the <code>Eq</code> class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: <strong>typeclasses are functions from types to (runtime) terms</strong>.</p><p>What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called <code>TypeOf</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Given these instances, we can observe that they do what we expect in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>Note that both the <code>TypeOf Bool</code> and <code>TypeOf Char</code> instances ignore the argument to <code>typeOf</code> altogether. This makes sense, as the whole point of the <code>TypeOf</code> class is to get access to <em>type</em> information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This typeclass definition is a little unusual, as the type parameter <code>a</code> doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>the full type of the <code>show</code> method is implicitly extended with a <code>Show a</code> constraint to yield:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Furthermore, if we write <code>forall</code>s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the <em>full</em> type of <code>show</code>:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>In the same vein, we can write out the full type of <code>typeOf</code>, as given by our new definition of <code>TypeOf</code>:</p><pre><code class="pygments"><span class="nf">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This type is still unusual, as the <code>a</code> type parameter doesn’t appear anywhere to the right of the <code>=&gt;</code> arrow. This makes the type parameter trivially <em>ambiguous</em>, which is to say it’s impossible for GHC to infer what <code>a</code> should be at any call site. Fortunately, <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html">we can use <code>TypeApplications</code></a> to pass a type for <code>a</code> directly, as we can see in the updated definition of <code>TypeOf (a, b)</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Once again, we can test out our new definitions in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="kt">Bool</span> +<span class="s">"Bool"</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Bool</span><span class="p">,</span><span class="w"> </span><span class="kt">Char</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our <code>typeOf</code> function is, quite literally, a function that accepts a single type as an argument and returns a term-level <code>String</code>. Of course, the <code>TypeOf</code> typeclass is not a particularly <em>useful</em> example of such a function, but it demonstrates how easy it is to construct.</p><h3><a name="type-level-interpreters"></a>Type-level interpreters</h3><p>One important consequence of eliminating the value-level argument of <code>typeOf</code> is that there is no need for its argument type to actually be <em>inhabited</em>. For example, consider the <code>TypeOf</code> instance on <code>Void</code> from <code>Data.Void</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Void</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Void"</span></code></pre><p>This above instance is no different from the ones on <code>Bool</code> and <code>Char</code> even though <code>Void</code> is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of <code>TypeOf</code> on lists:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"["</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"]"</span></code></pre><p>If <code>typeOf</code> required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type <code>a</code> to recursively apply <code>typeOf</code> to. But since <code>typeOf</code> only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.</p><p>A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Z</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="n">a</span></code></pre><p>It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:</p><ul><li><p><code>Z</code> is a type that represents 0.</p></li><li><p><code>S Z</code> is a type that represents 1.</p></li><li><p><code>S (S Z)</code> is a type that represents 2.</p></li></ul><p>And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Numeric.Natural</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>As its name implies, <code>reifyNat</code> reifies a type-level natural number encoded using our datatypes above into a term-level <code>Natural</code> value:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="kt">Z</span> +<span class="mi">0</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span> +<span class="mi">1</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="mi">2</span></code></pre><p>One way to think about <code>reifyNat</code> is as an <em>interpreter</em> of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.</p><h3><a name="overlapping-instances"></a>Overlapping instances</h3><p>Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for <code>Show (Maybe a)</code>, you aren’t supposed to <em>also</em> write an instance for <code>Show (Maybe Bool)</code>, since it isn’t clear whether <code>show (Just True)</code> should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.</p><p>Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.</p><p>As a simple example, suppose we wanted to write a typeclass that checks whether a given type is <code>()</code> or not:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:</p><pre><code class="pygments"><span class="c1">-- not actually valid Haskell, just an example</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>But if we try to translate this to typeclass instances, we’ll get a problem:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> This means GHC will complain about instance overlap if we try to evaluate <code>isUnit @()</code>:</p><pre><code>ghci&gt; isUnit @() + +error: + • Overlapping instances for IsUnit () + arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance IsUnit () +</code></pre><p>To fix this, we have to explicitly mark <code>IsUnit ()</code> as overlapping:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>Now GHC accepts the expression without complaint:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="nb">()</span> +<span class="kt">True</span></code></pre><p>What does the <code>{-# OVERLAPPING #-}</code> pragma do, exactly? The gory details are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances">spelled out in the GHC User’s Guide</a>, but the simple explanation is that <code>{-# OVERLAPPING #-}</code> relaxes the overlap checker as long as the instance is <em>strictly more specific</em> than the instance(s) it overlaps with. In this case, that is true: <code>IsUnit ()</code> is trivially more specific than <code>IsUnit a</code>, since the former only matches <code>()</code> while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.</p><p>Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>The intent of <code>guardUnit</code> is to use <code>isUnit</code> to detect if its argument is of type <code>()</code>, and if it is, to return an error. However, even though we marked <code>IsUnit ()</code> overlapping, we still get an overlapping instance error:</p><pre><code>error: + • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance [overlapping] IsUnit () + • In the expression: isUnit @a +</code></pre><p>What gives? The problem is that GHC simply doesn’t know what type <code>a</code> is when compiling <code>guardUnit</code>. It <em>could</em> be instantiated to <code>()</code> where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.</p><p>This behavior is actually a very, very good thing. If GHC were to blindly pick the <code>IsUnit a</code> instance in this case, then <code>guardUnit</code> would always take the <code>False</code> branch, even when passed a value of type <code>()</code>! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when <code>{-# OVERLAPPING #-}</code> annotations are used, so it’s important to keep their limitations in mind.</p><p>As it happens, in this particular case, the error is easily remedied. We simply have to add an <code>IsUnit</code> constraint to the type signature of <code>guardUnit</code>:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now picking the right <code>IsUnit</code> instance is deferred to the place where <code>guardUnit</code> is used, and the definition is accepted.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><h3><a name="type-families-are-functions-from-types-to-types"></a>Type families are functions from types to types</h3><p>In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE TypeFamilies #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>The above is a <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families">closed type family</a>, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of <code>Sum</code> would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="nf">sum</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span> +<span class="nf">sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="nf">sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="n">sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.</p><p>To test our definition of <code>Sum</code> in GHCi, we can use <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind">the <code>:kind!</code> command</a>, which prints out a type and its kind after reducing it as much as possible:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span></code></pre><p>We can also combine <code>Sum</code> with our <code>ReifyNat</code> class from earlier:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.</p><h3><a name="example-1-generalized-concat"></a>Example 1: Generalized <code>concat</code></h3><p>Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a <code>flatten</code> function similar to like-named functions provided by many dynamically-typed languages. In those languages, <code>flatten</code> is like <code>concat</code>, but it works on a list of arbitrary depth. For example, we might use it like this:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]</span></code></pre><p>In Haskell, lists of different depths have different types, so multiple levels of <code>concat</code> have to be applied explicitly. But using TMP, we can write a generic <code>flatten</code> function that operates on lists of any depth!</p><p>Since this is <em>typeclass</em> metaprogramming, we’ll unsurprisingly begin with a typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="o">???</span><span class="p">]</span></code></pre><p>Our first challenge is writing the return type of <code>flatten</code>. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>Now we can write our <code>Flatten</code> instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>The inductive case is when the type is a nested list, in which case we want to apply <code>concat</code> and recur:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">(</span><span class="n">concat</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Sadly, if we try to compile these definitions, GHC will reject our <code>Flatten [a]</code> instance:</p><pre><code>error: + • Couldn't match type ‘a’ with ‘ElementOf [a]’ + ‘a’ is a rigid type variable bound by + the instance declaration + Expected type: [ElementOf [a]] + Actual type: [a] + • In the expression: x + In an equation for ‘flatten’: flatten x = x + In the instance declaration for ‘Flatten [a]’ + | + | flatten x = x + | ^ +</code></pre><p>At first blush, this error looks very confusing. Why doesn’t GHC think <code>a</code> and <code>ElementOf [a]</code> are the same type? Well, consider what would happen if we picked a type like <code>[Int]</code> for <code>a</code>. Then <code>[a]</code> would be <code>[[Int]]</code>, a nested list, so the first case of <code>ElementOf</code> would apply. Therefore, GHC refuses to pick the second equation of <code>ElementOf</code> so hastily.</p><p>In this particular case, we might think that’s rather silly. After all, if <code>a</code> were <code>[Int]</code>, then GHC wouldn’t have picked the <code>Flatten [a]</code> instance to begin with, it would pick the more specific <code>Flatten [[a]]</code> instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.</p><p>Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our <code>Flatten [a]</code> instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>This is a <em>type equality constraint</em>. Type equality constraints are written with the syntax <code>a ~ b</code>, and they state that <code>a</code> must be the same type as <code>b</code>. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that <code>ElementOf [a]</code> must always be <code>a</code>, which allows the instance to typecheck.</p><p>Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must <em>eventually</em> be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the <code>Flatten [a]</code> instance is selected, GHC will know that <code>a</code> is <em>not</em> a list type, and it will be able to reduce <code>ElementOf [a]</code> to <code>a</code> without difficulty. Indeed, we can see this for ourselves by using <code>flatten</code> in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span></code></pre><p>It works! But why do we need the type annotation on <code>1</code>? If we leave it out, we get a rather hairy type error:</p><pre><code>error: + • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ + Expected type: [ElementOf [a]] + Actual type: [ElementOf [a0]] + NB: ‘ElementOf’ is a non-injective type family + The type variable ‘a0’ is ambiguous +</code></pre><p>The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a <code>Num [a]</code> instance, in which case <code>1</code> could actually have a list type, and either case of <code>ElementOf</code> could match depending on the choice of <code>Num</code> instance. Of course, no such <code>Num</code> instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.</p><p>This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call <code>flatten</code> on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.</p><p>That wrinkle aside, <code>flatten</code> is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.</p><h4><a name="typeclasses-as-compile-time-code-generation"></a>Typeclasses as compile-time code generation</h4><p>Presented with the above definition of <code>Flatten</code>, it might not be immediately obvious how to think about <code>Flatten</code> as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, <code>Eq</code> or <code>Show</code>) than the <code>TypeOf</code> and <code>ReifyNat</code> classes we defined above.</p><p>One useful way to shift our perspective is to consider equivalent <code>Flatten</code> instances written using point-free style:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">id</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">concat</span></code></pre><p>These definitions of <code>flatten</code> no longer (syntactically) depend on term-level arguments, just like our definitions of <code>typeOf</code> and <code>reifyNat</code> didn’t accept any term-level arguments above. This allows us to consider what <code>flatten</code> might “expand to” given a type argument alone:</p><ul><li><p><code>flatten @[Int]</code> is just <code>id</code>, since the <code>Flatten [a]</code> instance is selected.</p></li><li><p><code>flatten @[[Int]]</code> is <code>flatten @[Int] . concat</code>, since the <code>Flatten [[a]]</code> instance is selected. That then becomes <code>id . concat</code>, which can be further simplified to just <code>concat</code>.</p></li><li><p><code>flatten @[[[Int]]]</code> is <code>flatten @[[Int]] . concat</code>, which simplifies to <code>concat . concat</code> by the same reasoning above.</p></li><li><p><code>flatten @[[[[Int]]]]</code> is then <code>concat . concat . concat</code>, and so on.</p></li></ul><p>This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of <code>flatten</code> takes a type as an argument and produces some number of composed <code>concat</code>s as a result. From this perspective, <code>Flatten</code> is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.</p><p>This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name <em>metaprogramming</em>. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.</p><h2><a name="part-2-generic-programming"></a>Part 2: Generic programming</h2><p>Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.</p><p>In the previous section, we discussed how to use TMP to write a generic <code>flatten</code> operation. In this section, we’ll aim a bit higher: totally generic functions that operate on <em>arbitrary</em> datatypes.</p><h3><a name="open-type-families-and-associated-types"></a>Open type families and associated types</h3><p>Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, <em>open type families</em>. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using <code>type instance</code> declarations. For example, we could define our <code>Sum</code> family from above like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>In the case of <code>Sum</code>, this would not be very useful, and indeed, <code>Sum</code> is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.</p><p>This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a <code>Key</code> open type family that relates types to the types used to index them:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span></code></pre><p>This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>In this case, anyone could define their own data structure, define instances of <code>Key</code> and <code>HasKey</code> for their data structure, and use <code>hasKey</code> to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>An open family declared inside a typeclass like this is called an <em>associated type</em>. It works exactly the same way as the separate definitions of <code>Key</code> and <code>HasKey</code>, it just uses a different syntax. Note that although the <code>family</code> and <code>instance</code> keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).</p><p>Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like <a href="https://hackage.haskell.org/package/mono-traversable"><code>mono-traversable</code></a> are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.</p><p>However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes <em>heavy</em> use of open type families: datatype-generic programming.</p><h3><a name="example-2-datatype-generic-programming"></a>Example 2: Datatype-generic programming</h3><p><em>Datatype-generic programming</em> refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include</p><ul><li><p>equality, comparison, and hashing,</p></li><li><p>recursive traversal of self-similar data structures, and</p></li><li><p>serialization and deserialization,</p></li></ul><p>among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on <em>any</em> datatype.</p><p>In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.</p><h4><a name="generic-datatype-representations"></a>Generic datatype representations</h4><p>At the heart of the <code>Generic</code> class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Authentication</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AuthBasic</span><span class="w"> </span><span class="kt">Username</span><span class="w"> </span><span class="kt">Password</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AuthSSH</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>then we have a type that is essentially equivalent to this one:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>If we know how to define a function on a nested tree built out of <code>Either</code>s and pairs, then we know how to define it on <em>any</em> such datatype! This is where TMP comes in: recall the way we viewed <code>Flatten</code> as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?</p><p>The answer to that question is <em>yes</em>. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, <code>numFields (AuthBasic "alyssa" "pass1234")</code> would return <code>2</code>, while <code>numFields (AuthSSH "&lt;key&gt;")</code> would return <code>1</code>. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.</p><p>We’ll start by using TMP to implement a “generic” version of <code>numFields</code> that operates on trees of <code>Either</code>s and pairs as described above:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="c1">-- base case: leaf value</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>Just like our <code>Flatten</code> class from earlier, <code>GNumFields</code> uses the type-level structure of its argument to choose what to do:</p><ul><li><p>If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.</p></li><li><p>If we find <code>Left</code> or <code>Right</code>, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.</p></li><li><p>In the case of any other value, we’re at a “leaf” in the tree of <code>Either</code>s and pairs, which corresponds to a single field, so we just return <code>1</code>.</p></li></ul><p>Now if we call <code>gnumFields (Left ("alyssa", "pass1234"))</code>, we’ll get <code>2</code>, and if we call <code>gnumFields (Right "&lt;key&gt;")</code>, we’ll get <code>1</code>. All that’s left to do is write a bit of code that converts our <code>Authentication</code> type to a tree of <code>Either</code>s and pairs:</p><pre><code class="pygments"><span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericizeAuthentication</span></code></pre><p>Now we get the results we want on our <code>Authentication</code> type using <code>numFieldsAuthentication</code>, but we’re not done yet, since it only works on <code>Authentication</code> values. Is there a way to define a generic <code>numFields</code> function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFields</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericize</span></code></pre><p>Now <code>numFields (AuthBasic "alyssa" "pass1234")</code> returns <code>2</code>, as desired, and it will <em>also</em> work with any datatype that provides a <code>Generic</code> instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:</p><ul><li><p>First, we define the <code>Generic</code> class, comprised of two parts:</p><ol><li><p>The <code>Rep a</code> associated type maps a type <code>a</code> onto its generic, sums-of-products representation, i.e. one built out of combinations of <code>Either</code> and pairs.</p></li><li><p>The <code>genericize</code> method converts an actual <em>value</em> of type <code>a</code> to the equivalent value using the sums-of-products representation.</p></li></ol></li><li><p>Next, we define a <code>Generic</code> instance for <code>Authentication</code>. <code>Rep Authentication</code> is the sums-of-products representation we described above, and <code>genericize</code> is likewise <code>genericizeAuthentication</code> from above.</p></li><li><p>Finally, we define <code>numFields</code> as a function with a <code>GNumFields (Rep a)</code> constraint. This is where all the magic happens:</p><ul><li><p>When we apply <code>numFields</code> to a datatype, <code>Rep</code> retrieves its generic, sums-of-products representation type.</p></li><li><p>The <code>GNumFields</code> class then uses various TMP techniques we’ve already described so far in this blog post to generate a <code>numFields</code> implementation on the fly from the structure of <code>Rep a</code>.</p></li><li><p>Finally, that generated <code>numFields</code> implementation is applied to the genericized term-level value, and the result is produced.</p></li></ul></li></ul><p>After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) <code>numFields</code> operation. Surely just defining <code>numFields</code> on each type directly would be far easier? Indeed, if we were just considering <code>numFields</code>, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined <code>numFields</code>, and all of them would automatically work on <code>Authentication</code> because they all leverage the same <code>Generic</code> instance!</p><p>This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation <em>once</em>, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.</p><h4><a name="improving-our-definition-of-generic"></a>Improving our definition of <code>Generic</code></h4><p>You may note that the definition of <code>Generic</code> provided above does not match the one in <code>GHC.Generic</code>. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a <code>GHC.Generics</code> tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.</p><h5><a name="distinguishing-leaves-from-the-spine"></a>Distinguishing leaves from the spine</h5><p>One problem with our version of <code>Generic</code> is that it provides no way to distinguish an <code>Either</code> or pair that should be considered a “leaf”, as in a type like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">A</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">B</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">)</span></code></pre><p>Given this type, <code>Rep Foo</code> should be <code>Either (Either Int String) (Char, Bool)</code>, and <code>numFields (Right ('a', True))</code> will erroneously return <code>2</code> rather than <code>1</code>. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">getLeaf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Now our <code>Generic</code> instances look like this:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">PublicKey</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">key</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">))</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">A</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">B</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Since the <code>Leaf</code> constructor now distinguishes a leaf, rather than the absence of an <code>Either</code> or <code>(,)</code> constructor, we’ll have to update our <code>GNumFields</code> instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.</p><h5><a name="handling-empty-constructors"></a>Handling empty constructors</h5><p>Suppose we have a type with nullary data constructors, like the standard <code>Bool</code> type:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>How do we write a <code>Generic</code> instance for <code>Bool</code>? Using just <code>Either</code>, <code>(,)</code>, and <code>Leaf</code>, we can’t, but if we are willing to add a case for <code>()</code>, we can use it to denote nullary constructors:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="nb">()</span></code></pre><p>In a similar vein, we could use <code>Void</code> to represent datatypes that don’t have any constructors at all.</p><h4><a name="continuing-from-here"></a>Continuing from here</h4><p>The full version of <code>Generic</code> has a variety of further improvements useful for generic programming, including:</p><ul><li><p>Support for converting from <code>Rep a</code> to <code>a</code>.</p></li><li><p>Special indication of self-recursive datatypes, making generic tree traversals possible.</p></li><li><p>Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.</p></li><li><p>Fully automatic generation of <code>Generic</code> instances via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric">the <code>DeriveGeneric</code> extension</a>, which reduces the per-type boilerplate to essentially nothing.</p></li></ul><p>The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.</p><h2><a name="part-3-dependent-typing"></a>Part 3: Dependent typing</h2><p>It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.</p><h3><a name="datatype-promotion"></a>Datatype promotion</h3><p>In part 1, we used uninhabited datatypes like <code>Z</code> and <code>S a</code> to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, <code>True</code> and <code>False</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">True</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">False</span></code></pre><p>Now we could define type families to provide operations on these types, such as <code>Not</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>However, this has some frustrating downsides:</p><ul><li><p>First, it’s simply inconvenient that we have to define these new <code>True</code> and <code>False</code> “dummy” types, which are completely distinct from the <code>Bool</code> type provided by the prelude.</p></li><li><p>More significantly, it means <code>Not</code> has a very unhelpful kind:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span></code></pre><p>Even though <code>Not</code> is only <em>supposed</em> to be applied to <code>True</code> or <code>False</code>, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like <code>Not Char</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span> +<span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span></code></pre><p>Rather than getting an error, GHC simply spits <code>Not Char</code> back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.</p></li></ul><p>One way to think about <code>Not</code> is that it is largely <em>dynamically kinded</em> in the same way some languages are dynamically typed. That isn’t entirely true, as we technically <em>will</em> get a kind error if we try to apply <code>Not</code> to a type constructor rather than a type, such as <code>Maybe</code>:</p><pre><code>ghci&gt; :kind! Not Maybe + +&lt;interactive&gt;:1:5: error: + • Expecting one more argument to ‘Maybe’ + Expected a type, but ‘Maybe’ has kind ‘* -&gt; *’ +</code></pre><p>…but <code>*</code> is still a very big kind, much bigger than we would like to permit for <code>Not</code>.</p><p>To help with both these problems, GHC provides <em>datatype promotion</em> via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html">the <code>DataKinds</code> language extension</a>. The idea is that for each normal, non-GADT type definition like</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>then in addition to the normal type constructor and value constructors, GHC also defines several <em>promoted</em> constructors:</p><ul><li><p><code>Bool</code> is allowed as both a type and a kind.</p></li><li><p><code>'True</code> and <code>'False</code> are defined as new types of kind <code>Bool</code>.</p></li></ul><p>We can see this in action if we remove our <code>data True</code> and <code>data False</code> declarations and adjust our definition of <code>Not</code> to use promoted constructors:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DataKinds #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;True</span></code></pre><p>Now the inferred kind of <code>Not</code> is no longer <code>* -&gt; *</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>Consequently, we will now get a kind error if we attempt to apply <code>Not</code> to anything other than <code>'True</code> or <code>'False</code>:</p><pre><code>ghci&gt; :kind! Not Char + +&lt;interactive&gt;:1:5: error: + • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ +</code></pre><p>This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">&#39;Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">&#39;S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>Note that we need to add an explicit kind signature on the definition of the <code>ReifyNat</code> typeclass, since otherwise GHC will assume <code>a</code> has kind <code>*</code>, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that <code>Z</code> and <code>S</code> are related, this prevents someone from coming along and defining a nonsensical instance like <code>ReifyNat Char</code>, which previously would have been allowed but will now be rejected with a kind error.</p><p>Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.</p><h3><a name="gadts-and-proof-terms"></a>GADTs and proof terms</h3><p>So far in this blog post, we have discussed several different function-like things:</p><ul><li><p>Ordinary Haskell functions are functions from terms to terms.</p></li><li><p>Type families are functions from types to types.</p></li><li><p>Typeclasses are functions from types to terms.</p></li></ul><p>A curious reader may wonder about the existence of a fourth class of function:</p><ul><li><p><em>???</em> are functions from terms to types.</p></li></ul><p>To reason about what could go in the <em>???</em> above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?</p><p>In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.</p><p>GADTs<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup> are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html">described in detail in the GHC User’s Guide</a>, but the key idea for our purposes is that <em>pattern-matching on a GADT constructor can refine type information</em>. Here’s a simple, silly example:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Bool</span> +<span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Int</span> + +<span class="nf">doSomething</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">x</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span></code></pre><p>Here, <code>WhatIsIt</code> is a datatype with two nullary constructors, <code>ABool</code> and <code>AnInt</code>, similar to a normal, non-GADT datatype like this one:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AnInt</span></code></pre><p>What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, <code>ABool</code> and <code>AnInt</code> would both have the type <code>forall a. WhatIsIt a</code>, but in the GADT definition, we explicitly fix <code>a</code> to <code>Bool</code> in the type of <code>ABool</code> and to <code>Int</code> in the type of <code>AnInt</code>.</p><p>This simple feature allows us to do very interesting things. The <code>doSomething</code> function is polymorphic in <code>a</code>, but on the right-hand side of the first equation, <code>x</code> has type <code>Bool</code>, while on the right-hand side of the second equation, <code>x</code> has type <code>Int</code>. This is because the <code>WhatIsIt a</code> argument effectively constrains the type of <code>a</code>, as we can see by experimenting with <code>doSomething</code> in GHCi:</p><pre><code>ghci&gt; doSomething ABool True +False +ghci&gt; doSomething AnInt 10 +11 +ghci&gt; doSomething AnInt True + +error: + • Couldn't match expected type ‘Int’ with actual type ‘Bool’ + • In the second argument of ‘doSomething’, namely ‘True’ + In the expression: doSomething AnInt True + In an equation for ‘it’: it = doSomething AnInt True +</code></pre><p>One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The <code>ABool</code> constructor is a proof of <code>a ~ Bool</code>, while the <code>AnInt</code> constructor is a proof of <code>a ~ Int</code>. When you construct <code>ABool</code> or <code>AnInt</code>, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.</p><p>GADTs can be much more sophisticated than our simple <code>WhatIsIt</code> type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:</p><pre><code class="pygments"><span class="kr">infixr</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span> + +<span class="kr">data</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">HCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This type is a <em>heterogenous list</em>, a list that can contain elements of different types:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">t</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Num</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[Bool, [Char]</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>An <code>HList</code> is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total <code>head</code> function on <code>HList</code>s like this:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Remarkably, GHC does not complain that this definition of <code>head</code> is non-exhaustive. Since we specified that the argument must be of type <code>HList (a ': as)</code> in the type signature for <code>head</code>, GHC knows that the argument <em>cannot</em> be <code>HNil</code> (which would have the type <code>HList '[]</code>), so it doesn’t ask us to handle that case.</p><p>These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.</p><h4><a name="proofs-that-work-together"></a>Proofs that work together</h4><p>This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an <code>HList</code> of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with <code>head</code>, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.</p><p>However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a <em>proof term</em> that contains no values, it just encapsulates type equalities on a type-level list:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a]</span> +<span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b]</span> +<span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b, c]</span></code></pre><p>We call it a proof term because a value of type <code>OneToThree a b c as</code> constitutes a <em>proof</em> that <code>as</code> has exactly 1, 2, or 3 elements. Using <code>OneToThree</code>, we can write a function that accepts an <code>HList</code> accompanied by a proof term:</p><pre><code class="pygments"><span class="nf">sumUpToThree</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">z</span></code></pre><p>As with <code>head</code>, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:</p><ul><li><p>When we match on the <code>OneToThree</code> proof term, information flows from the term level to the type level, refining the type of <code>as</code> in that branch.</p></li><li><p>The refined type of <code>as</code> then flows back down to the term level, restricting the shape the <code>HList</code> can take and refinine the set of patterns we have to match.</p></li></ul><p>Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an <code>HList</code> has an even number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This is a proof which itself has inductive structure: <code>EvenCons</code> takes a proof that <code>as</code> has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an <code>HList</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span> + +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Once again, this definition is completely exhaustive, and we can show that it works in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="kt">EvenNil</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.</p><h4><a name="proof-inference"></a>Proof inference</h4><p>While our definition of <code>pairUp</code> is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the <code>Even</code> proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.</p><p>Remember that typeclasses are functions from types to terms. As its happens, a value of type <code>Even as</code> can be mechanically produced from the structure of the type <code>as</code>. This suggests that we could use TMP to automatically generate <code>Even</code> proofs, and indeed, we can. In fact, it’s not at all complicated:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>We can now adjust our <code>pairUp</code> function to use <code>IsEven</code> instead of an explicit <code>Even</code> argument:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>This is essentially identical to its old definition, but by acquiring the proof via <code>IsEven</code> rather than passing it explicitly, we can call <code>pairUp</code> without having to construct a proof manually:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This is rather remarkable. Using TMP, we are able to get GHC to <em>automatically construct a proof that a list is even</em>, with no programmer guidance beyond writing the <code>IsEven</code> typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: <code>IsEven</code> is a function that accepts a type-level list and generates an <code>Even</code> proof term.</p><p>From this perspective, <strong>typeclasses are a way of specifying a proof search algorithm</strong> to the compiler. In the case of <code>IsEven</code>, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.</p><h3><a name="aside-gadts-versus-type-families"></a>Aside: GADTs versus type families</h3><p>Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.</p><p>Consider again the type of the <code>pairUp</code> function above (without the typeclass for simplicity):</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>We used both a GADT, <code>Even</code>, and a type family, <code>PairUp</code>. But we could have, in theory, used <em>only</em> a GADT and eliminated the type family altogether. Consider this variation on the <code>Even</code> proof term:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="p">((</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span></code></pre><p>This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of <code>as</code> as an “input” parameter and <code>bs</code> as an “output” parameter. The idea is that any <code>EvenPairs</code> proof relates both an even-length list type and its paired up equivalent:</p><ul><li><p><code>EvenNil</code> has type <code>EvenPairs '[] '[]</code>,</p></li><li><p><code>EvenCons EvenNil</code> has type <code>EvenPairs '[a, b] '[(a, b)]</code>,</p></li><li><p><code>EvenCons (EvenCons EvenNil)</code> has type <code>EvenPairs '[a, b, c, d] '[(a, b), (c, d)]</code>,</p></li><li><p>…and so on.</p></li></ul><p>This allows us to reformulate our <code>pairUp</code> type signature this way:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">bs</span></code></pre><p>The definition is otherwise unchanged. The <code>PairUp</code> type family is completely gone, because now <code>EvenPairs</code> itself defines the relation. In this way, GADTs can be used like type-level functions!</p><p>The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Kind</span><span class="w"> </span><span class="p">(</span><span class="kt">Constraint</span><span class="p">)</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span></code></pre><p>The idea here is that <code>IsEvenTF as</code> produces a constraint can only be satisfied if <code>as</code> has an even number of elements, since that’s the only way it will eventually reduce to <code>()</code>, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting <code>IsEvenTF as =&gt;</code> in a type signature successfully restricts <code>as</code> to be an even-length list, but it doesn’t allow us to write <code>pairUp</code>. To see why, we can try the following definition:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Unlike the version using the GADT, this version of <code>pairUp</code> is not considered exhaustive:</p><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil +</code></pre><p>This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by <code>IsEvenTF</code> provides no term-level evidence about the shape of <code>as</code>, so we can’t branch on it the way we can branch on the <code>Even</code> GADT.<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> (In a sense, <code>IsEvenTF</code> is doing <a href="/blog/2019/11/05/parse-don-t-validate/">validation, not parsing</a>.)</p><p>For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.</p><h3><a name="guiding-type-inference"></a>Guiding type inference</h3><p>So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.</p><p>However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.</p><p>To illustrate what that can look like, suppose we want to use TMP to generate an <code>HList</code> full of <code>()</code> values of an arbitrary length:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Testing in GHCi, we can see it behaves as desired:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[(), (), ()]</span> +<span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>Now suppose we write a function that accepts a list containing exactly one element and returns it:</p><pre><code class="pygments"><span class="nf">unsingleton</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[a]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unsingleton</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Naturally, we would expect these to compose without a hitch. If we write <code>unsingleton unitList</code>, our TMP should generate a list of length 1, and we should get back <code>()</code>. However, it may surprise you to learn that <em>isn’t</em>, in fact, what happens:<sup><a href="#footnote-6" id="footnote-ref-6-1">6</a></sup></p><pre><code>ghci&gt; unsingleton unitList + +error: + • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ + prevents the constraint ‘(UnitList '[a0])’ from being solved. + Probable fix: use a type annotation to specify what ‘a0’ should be. + These potential instances exist: + instance UnitList as =&gt; UnitList (() : as) +</code></pre><p>What went wrong? The type error says that <code>a0</code> is ambiguous, but it only lists a single matching <code>UnitList</code> instance—the one we want—so how can it be ambiguous which one to select?</p><p>The problem stems from the way we defined <code>UnitList</code>. When we wrote the instance</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span></code></pre><p>we said the first element of the type-level list must be <code>()</code>, so there’s nothing stopping someone from coming along and defining another instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="kt">Int</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>In that case, GHC would have no way to know which instance to pick. Nothing in the type of <code>unsingleton</code> forces the element in the list to have type <code>()</code>, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.</p><p>Of course, this isn’t what we want. The <code>UnitList</code> class is supposed to <em>always</em> return a list of <code>()</code> values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Here we’ve changed the instance so that it has the shape <code>UnitList (a ': as)</code>, with a type variable in place of the <code>()</code>, but we also added an equality constraint that forces <code>a</code> to be <code>()</code>. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unsingleton</span><span class="w"> </span><span class="n">unitList</span> +<span class="nb">()</span></code></pre><p>To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="o">&lt;</span><span class="n">constraints</span><span class="o">&gt;</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">C</span><span class="w"> </span><span class="o">&lt;</span><span class="n">types</span><span class="o">&gt;</span></code></pre><p>The part to the left of the <code>=&gt;</code> is known as the <em>instance context</em>, while the part to the right is known as the <em>instance head</em>. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, <strong>only the instance head matters, and the instance context is completely ignored</strong>. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.</p><p>This explains why our two <code>UnitList</code> instances behave differently:</p><ul><li><p>Given the instance head <code>UnitList (() ': as)</code>, GHC won’t select the instance unless it knows the first element of the list is <code>()</code>.</p></li><li><p>But given the instance head <code>UnitList (a ': as)</code>, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.</p></li></ul><p>After the <code>UnitList (a ': as)</code> instance is selected, GHC attempts to solve the constraints in the instance context, including the <code>a ~ ()</code> constraint. This <em>forces</em> <code>a</code> to be <code>()</code>, resolving the ambiguity and allowing type inference to proceed.</p><p>This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:</p><ul><li><p>If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.</p></li><li><p>But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell <em>you</em> what type this is,” effectively giving you a role in type inference itself.</p></li></ul><p>From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.</p><p>Given all of the above, consider again the definition of <code>IsEven</code> from earlier:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an <code>IsEven (a ': b0)</code> constraint, where <code>b0</code> is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an <code>IsEven '[a]</code> instance in the future.</p><p>To fix this, we can apply the same trick we used for <code>UnitList</code>, just in a slightly different way:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Again, the idea is to move the type information we <em>learn</em> from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can <strong>dramatically</strong> improve type inference in programs that make heavy use of TMP.</p><h3><a name="example-3-subtyping-constraints"></a>Example 3: Subtyping constraints</h3><p>At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at <a href="https://hasura.io/">Hasura</a>, I had the opportunity to design an internal parser combinator library that captures aspects of the <a href="https://graphql.org/">GraphQL</a> type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.</p><p>Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our <code>GQLKind</code> datatype has three cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLKind</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Both</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Input</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Output</span></code></pre><p>We use <code>DataKind</code>-promoted versions of this <code>GQLKind</code> type as a parameter to a <code>GQLType</code> GADT:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">TScalar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Both</span> +<span class="w"> </span><span class="kt">TInputObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">InputObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Input</span> +<span class="w"> </span><span class="kt">TIObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Output</span> +<span class="w"> </span><span class="c1">-- ...and so on...</span></code></pre><p>This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type <code>GQLType 'Input</code>, we can’t pass a <code>GQLType 'Both</code>, even though we really ought to be able to.</p><p>To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span></code></pre><p>The first case, <code>KRefl</code>, states that every kind is trivially a subkind of itself. The second case, <code>KBoth</code>, states that <code>Both</code> is a subkind of any kind at all. (This is a particularly literal example of <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">using a type to define axioms</a>.) The next step is to use TMP to implement proof inference:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KBoth</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span></code></pre><p>These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that <code>k</code> is a superkind of <code>'Input</code> or <code>'Output</code>, type inference will force them to be equal.</p><p>Using <code>IsSubKind</code>, we can easily resolve the problem described above. Rather than write a function with a type like this:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>…we simply use an <code>IsSubKind</code> constraint, instead:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now both <code>'Input</code> and <code>'Both</code> kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. <em>Consuming</em> the <code>SubKind</code> proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SelectionSet</span></code></pre><p>This type family is used to determine what a <code>GQLParser k a</code> actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>.</p><p>Fortunately, that is very easy to do using <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html">the <code>(:~:)</code> type from <code>Data.Type.Equality</code> in <code>base</code></a> to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">a</span></code></pre><p>Just as with any other GADT, <code>(:~:)</code> can be used to pack up type equalities and unpack them later; <code>a :~: b</code> just happens to be the GADT that corresponds precisely to the equality <code>a ~ b</code>. Using <code>(:~:)</code>, we can write a reusable proof that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>:</p><pre><code class="pygments"><span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="o">@</span><span class="kt">&#39;Input</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span></code></pre><p>This function is a very simple proof by cases, where <code>Refl</code> can be read as “Q.E.D.”:</p><ul><li><p>In the first case, matching on <code>KRefl</code> refines <code>k</code> to <code>'Input</code>, and <code>ParserInput 'Input</code> is <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li><li><p>Likewise, in the second case, matching on <code>KBoth</code> refines <code>k</code> to <code>'Both</code>, and <code>ParserInput 'Both</code> is also <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li></ul><p>This <code>inputParserInput</code> helper allows functions like <code>nullable</code>, which internally need <code>ParserInput k ~ InputValue</code>, to take the form</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nullable</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">inputParserInput</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ...implementation goes here... -}</span></code></pre><p>Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without <code>IsSubKind</code> doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!</p><h2><a name="wrapping-up-and-closing-thoughts"></a>Wrapping up and closing thoughts</h2><p>So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:</p><ul><li><p>Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.</p></li><li><p>Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.</p></li><li><p>Though I’ve called the technique “<em>typeclass</em> metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.</p></li><li><p>Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.</p></li></ul><p>The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.</p><p>Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.</p><p>It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:</p><ul><li><p>As mentioned earlier in this blog post, <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">the <code>GHC.Generics</code> module documentation</a> is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.</p></li><li><p>I have long believed that <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/">the GHC User’s Guide</a> is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.</p></li><li><p>Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the <a href="https://hackage.haskell.org/package/singletons">singletons</a> library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, <a href="https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf">the accompanying paper</a> is definitely worth a read if you’d like to go down that route.)</p></li></ul><p>Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.</p><ol class="footnotes"><li id="footnote-1"><p>Not to be confused with C++’s <a href="https://en.wikipedia.org/wiki/Template_metaprogramming"><em>template</em> metaprogramming</a>, though there are significant similarities between the two techniques. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>There have been proposals to introduce ordered instances, known in the literature as <a href="https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf"><em>instance chains</em></a>, but as of this writing, GHC does not implement them. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Note that this also preserves an important property of the Haskell type system, parametricity. A function like <code>id :: a -&gt; a</code> shouldn’t be allowed to do different things depending on which type is chosen for <code>a</code>, which our first version of <code>guardUnit</code> tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Short for <em>generalized algebraic datatypes</em>, which is a rather unhelpful name for actually understanding what they are or what they’re for. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for <code>IsEvenTF</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">exists</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">as&#39;</span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">)</span></code></pre><p>The type refinement provided by matching on <code>HCons</code> would be enough for the second case of <code>IsEvenTF</code> to be selected, which would provide an equality proof that <code>as</code> has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. <a href="#footnote-ref-5-1">↩</a></p></li><li id="footnote-6"><p>Actually, I’ve cheated a little bit here, because <code>unsingleton unitList</code> really does typecheck in GHCi under normal circumstances. That’s because <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules">the <code>ExtendedDefaultRules</code> extension</a> is enabled in GHCi by default, which defaults ambiguous type variables to <code>()</code>, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned <code>ExtendedDefaultRules</code> off to illustrate the problem. <a href="#footnote-ref-6-1">↩</a></p></li></ol></article>Names are not type safety2020-11-01T00:00:00Z2020-11-01T00:00:00ZAlexis King<article><p>Haskell programmers spend a lot of time talking about <em>type safety</em>. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published <a href="/blog/2019/11/05/parse-don-t-validate/">Parse, Don’t Validate</a> as an initial stab towards bridging that gap.</p><p>The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s <code>newtype</code> construct. The idea is simple enough—the <code>newtype</code> keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this <em>sounds</em> like a simple and straightforward path to type safety. For example, one might consider using a <code>newtype</code> declaration to define a type for an email address:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This technique can provide <em>some</em> value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct <em>kind</em> of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.</p><p>And names are not type safety.</p><h2><a name="intrinsic-and-extrinsic-safety"></a>Intrinsic and extrinsic safety</h2><p>To illustrate the difference between constructive data modeling (discussed at length in my <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">previous blog post</a>) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">One</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Two</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Three</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Four</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Five</span></code></pre><p>We could then write some functions to convert between <code>Int</code> and our <code>OneToFive</code> type:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">One</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Two</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Three</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Four</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Five</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">3</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">5</span></code></pre><p>This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="kt">Int</span></code></pre><p>Just as before, we can provide <code>toOneToFive</code> and <code>fromOneToFive</code> functions, with identical types:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">otherwise</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="p">(</span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">n</span></code></pre><p>If we put these declarations in their own module and choose not to export the <code>OneToFive</code> constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.</p><p>To see why, suppose we write a function that consumes a <code>OneToFive</code> value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"first"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"second"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"third"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fifth"</span></code></pre><p>The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an <code>Int</code>—after all, it <em>is</em> an <code>Int</code>. An <code>Int</code> can of course contain many other values besides <code>1</code> through <code>5</code>, so we are forced to add an error case to satisfy the exhaustiveness checker:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromOneToFive</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"first"</span> +<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"second"</span> +<span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"third"</span> +<span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fifth"</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: bad OneToFive value"</span></code></pre><p>In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:</p><ul><li><p>The constructive datatype captures its invariants in such a way that they are <em>accessible</em> to downstream consumers. This frees our <code>ordinal</code> function from worrying about handling illegal values, as they have been made unutterable.</p></li><li><p>The newtype wrapper provides a smart constructor that <em>validates</em> the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting <code>Int</code>s.</p></li></ul><p>Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of <code>error</code> has punched a hole right through our type system. If we were to add another constructor to our <code>OneToFive</code> datatype,<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> the version of <code>ordinal</code> that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.</p><p>All of this is a consequence of the fact that the constructive modeling is <em>intrinsically</em> type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent <code>6</code> using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an <code>Int</code>; its meaning is specified extrinsically via the <code>toOneToFive</code> smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.</p><h3><a name="revisiting-non-empty-lists"></a>Revisiting non-empty lists</h3><p>Our <code>OneToFive</code> datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the <code>NonEmpty</code> datatype I’ve repeatedly highlighted in recent blog posts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>It may be illustrative to imagine a version of <code>NonEmpty</code> represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Foldable</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Just as with <code>OneToFive</code>, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for <code>NonEmpty</code> was the ability to write a safe version of <code>head</code>, but the newtype version requires another assertion:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">x</span> +<span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines <code>NonEmpty</code>, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.</p><h2><a name="newtypes-as-tokens"></a>Newtypes as tokens</h2><p>If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes <em>can</em> provide a sort of safety, just a weaker one.</p><p>The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a <em>trust boundary</em> where internal invariants are enforced by restricting clients to a safe API.</p><p>We can use the <code>NonEmpty</code> example from above to illustrate how this works. We refrain from exporting the <code>NonEmpty</code> constructor, and we provide <code>head</code> and <code>tail</code> operations that we trust to never actually fail:</p><pre><code class="pygments"><span class="kr">module</span><span class="w"> </span><span class="nn">Data.List.NonEmpty.Newtype</span> +<span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">NonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">cons</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">nonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">head</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">tail</span> +<span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">cons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span> +<span class="nf">cons</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span> + +<span class="nf">tail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>Since the only way to construct or consume <code>NonEmpty</code> values is to use the functions in <code>Data.List.NonEmpty.Newtype</code>’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like <em>tokens</em>: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case <code>head</code> and <code>tail</code>, to obtain the values contained within.</p><p>This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid <code>NonEmpty []</code> value. For this reason, the newtype approach to type safety does not on its own constitute a <em>proof</em> that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a <code>Generic</code> instance for <code>NonEmpty</code>:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DeriveGeneric #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">GHC.Generics</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span></code></pre><p>However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">GHC</span><span class="o">.</span><span class="kt">Generics</span><span class="o">.</span><span class="n">to</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">K1</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>This is a particularly extreme example, since derived <code>Generic</code> instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived <code>Read</code> instance:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">read</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="s">"NonEmpty []"</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:</p><ul><li><p>All invariants must be made clear to maintainers of the trusted module. For simple types, such as <code>NonEmpty</code>, the invariant is self-evident, but for more sophisticated types, comments are not optional.</p></li><li><p>Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.</p></li><li><p>Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.</p></li><li><p>Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.</p></li></ul><p>In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.</p><p>In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.</p><h2><a name="other-newtype-use-abuse-and-misuse"></a>Other newtype use, abuse, and misuse</h2><p>The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:</p><ul><li><p>Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the <code>Sum</code> and <code>Product</code> newtypes from <code>Data.Monoid</code> provide useful <code>Monoid</code> instances for numeric types.</p></li><li><p>In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The <code>Flip</code> newtype from <code>Data.Bifunctor.Flip</code> is a simple example, flipping the arguments of a <code>Bifunctor</code> so the <code>Functor</code> instance may operate on the other side:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runFlip</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.</p></li><li><p>More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a <code>ByteString</code> containing a secret key may be wrapped in a newtype (with a <code>Show</code> instance omitted) to discourage code from accidentally logging or otherwise exposing it.</p></li></ul><p>All of these applications are good ones, but they have little to do with <em>type safety.</em> The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually <em>prevents</em> misuse; any part of the program may inspect the value at any time.</p><p>Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">unArgumentName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSONKey</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSONKey</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">Hashable</span><span class="p">,</span><span class="w"> </span><span class="kt">ToTxt</span><span class="p">,</span><span class="w"> </span><span class="kt">Lift</span><span class="p">,</span><span class="w"> </span><span class="kt">Generic</span><span class="p">,</span><span class="w"> </span><span class="kt">NFData</span><span class="p">,</span><span class="w"> </span><span class="kt">Cacheable</span><span class="w"> </span><span class="p">)</span></code></pre><p>This newtype is useless noise. Functionally, it is completely interchangeable with its underlying <code>Name</code> type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an <code>ArgumentName</code>, since the enclosing field name already makes its role clear.</p><p>Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:</p><ul><li><p>Primarily, types distinguish <em>functional</em> differences between values. A value of type <code>NonEmpty a</code> is <em>functionally</em> distinct from a value of type <code>[a]</code>, since it is fundamentally structurally different and permits additional operations. In this sense, types are <em>structural</em>; they describe what values <em>are</em> in the internal world of the programming language.</p></li><li><p>Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate <code>Distance</code> and <code>Duration</code> types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.</p></li></ul><p>Note that both these uses are <em>pragmatic</em>; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system <em>is</em> a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like <code>ArgumentName</code>.</p><p>If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use <code>Name</code>, but in situations where the different label adds genuine clarity, one can always use a type alias:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span></code></pre><p>Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.</p><h2><a name="final-thoughts-and-related-reading"></a>Final thoughts and related reading</h2><p>I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.</p><p>Newtypes are one particular mechanism of defining <em>wrapper types</em>, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.</p><p>The catalyst that got me to finally sit down and write this was the recently-published <a href="https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/">Tagged is not a Newtype</a>. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, <code>Tagged</code> <em>is</em> a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.</p><p>Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.</p><p>And a name is not type safety.</p><ol class="footnotes"><li id="footnote-1"><p>Admittedly rather unlikely given its name, but bear with me through the contrived example. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Types as axioms, or: playing god with static types2020-08-13T00:00:00Z2020-08-13T00:00:00ZAlexis King<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more open2020-01-19T00:00:00Z2020-01-19T00:00:00ZAlexis King<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Parse, don’t validate2019-11-05T00:00:00Z2019-11-05T00:00:00ZAlexis King<article><p>Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.</p><p>However, about a month ago, <a href="https://twitter.com/lexi_lambda/status/1182242561655746560">I was reflecting on Twitter</a> about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:</p><div style="text-align: center; font-size: larger"><strong>Parse, don’t validate.</strong></div><h2><a name="the-essence-of-type-driven-design"></a>The essence of type-driven design</h2><p>Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.</p><h3><a name="the-realm-of-possibility"></a>The realm of possibility</h3><p>One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:</p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Void</span></code></pre><p>Is it possible to implement <code>foo</code>? Trivially, the answer is <em>no</em>, as <code>Void</code> is a type that contains no values, so it’s impossible for <em>any</em> function to produce a value of type <code>Void</code>.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘head’: Patterns not matched: [] +</code></pre><p>This message is helpfully pointing out that our function is <em>partial</em>, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is <code>[]</code>, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.</p><h3><a name="turning-partial-functions-total"></a>Turning partial functions total</h3><p>To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the <code>head</code> function, and we’ll start with the simplest one.</p><h4><a name="managing-expectations"></a>Managing expectations</h4><p>As established, <code>head</code> is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the <code>Maybe</code> type:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span></code></pre><p>This buys us the freedom we need to implement <code>head</code>—it allows us to return <code>Nothing</code> when we discover we can’t produce a value of type <code>a</code> after all:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>Problem solved, right? For the moment, yes… but this solution has a hidden cost.</p><p>Returning <code>Maybe</code> is undoubtably convenient when we’re <em>implementing</em> <code>head</code>. However, it becomes significantly less convenient when we want to actually use it! Since <code>head</code> always has the potential to return <code>Nothing</code>, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">configDirsList</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">configDirsList</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">cacheDir</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="n">cacheDir</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"should never happen; already checked configDirs is non-empty"</span></code></pre><p>When <code>getConfigurationDirectories</code> retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use <code>head</code> in <code>main</code> to get the first element of the list, the <code>Maybe FilePath</code> result still requires us to handle a <code>Nothing</code> case that we know will never happen! This is terribly bad for several reasons:</p><ol><li><p>First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?</p></li><li><p>Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.</p></li><li><p>Finally, and worst of all, this code is a bug waiting to happen! What if <code>getConfigurationDirectories</code> were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update <code>main</code>, and suddenly the “impossible” error becomes not only possible, but probable.</p></li></ol><p>The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically <em>prove</em> the <code>Nothing</code> case impossible, then a modification to <code>getConfigurationDirectories</code> that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.</p><h4><a name="paying-it-forward"></a>Paying it forward</h4><p>Clearly, our modified version of <code>head</code> leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, <code>head</code> should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?</p><p>Let’s look at the original (partial) type signature for <code>head</code> again:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, <code>[a]</code>). Instead of weakening the return type, we can <em>strengthen</em> the argument type, eliminating the possibility of <code>head</code> ever being called on an empty list in the first place.</p><p>To do this, we need a type that represents non-empty lists. Fortunately, the existing <code>NonEmpty</code> type from <code>Data.List.NonEmpty</code> is exactly that. It has the following definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>Note that <code>NonEmpty a</code> is really just a tuple of an <code>a</code> and an ordinary, possibly-empty <code>[a]</code>. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the <code>[a]</code> component is <code>[]</code>, the <code>a</code> component must always be present. This makes <code>head</code> completely trivial to implement:<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Unlike before, GHC accepts this definition without complaint—this definition is <em>total</em>, not partial. We can update our program to use the new implementation:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">FilePath</span><span class="p">)</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">nonEmpty</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="p">)</span></code></pre><p>Note that the redundant check in <code>main</code> is now completely gone! Instead, we perform the check exactly once, in <code>getConfigurationDirectories</code>. It constructs a <code>NonEmpty a</code> from a <code>[a]</code> using the <code>nonEmpty</code> function from <code>Data.List.NonEmpty</code>, which has the following type:</p><pre><code class="pygments"><span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>The <code>Maybe</code> is still there, but this time, we handle the <code>Nothing</code> case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a <code>NonEmpty FilePath</code> value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type <code>NonEmpty a</code> as being like a value of type <code>[a]</code>, plus a <em>proof</em> that the list is non-empty.</p><p>By strengthening the type of the argument to <code>head</code> instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:</p><ul><li><p>The code has no redundant checks, so there can’t be any performance overhead.</p></li><li><p>Furthermore, if <code>getConfigurationDirectories</code> changes to stop checking that the list is non-empty, its return type must change, too. Consequently, <code>main</code> will fail to typecheck, alerting us to the problem before we even run the program!</p></li></ul><p>What’s more, it’s trivial to recover the old behavior of <code>head</code> from the new one by composing <code>head</code> with <code>nonEmpty</code>:</p><pre><code class="pygments"><span class="nf">head&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fmap</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">nonEmpty</span></code></pre><p>Note that the inverse is <em>not</em> true: there is no way to obtain the new version of <code>head</code> from the old one. All in all, the second approach is superior on all axes.</p><h3><a name="the-power-of-parsing"></a>The power of parsing</h3><p>You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:</p><pre><code class="pygments"><span class="nf">validateNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span> + +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="n">xs</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span></code></pre><p>These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: <code>validateNonEmpty</code> always returns <code>()</code>, the type that contains no information, but <code>parseNonEmpty</code> returns <code>NonEmpty a</code>, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but <code>parseNonEmpty</code> gives the caller access to the information it learned, while <code>validateNonEmpty</code> just throws it away.</p><p>These two functions elegantly illustrate two different perspectives on the role of a static type system: <code>validateNonEmpty</code> obeys the typechecker well enough, but only <code>parseNonEmpty</code> takes full advantage of it. If you see why <code>parseNonEmpty</code> is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of <code>parseNonEmpty</code>’s name. Is it really <em>parsing</em> anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe <code>parseNonEmpty</code> is a bona-fide parser (albeit a particularly simple one).</p><p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and <code>parseNonEmpty</code> is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.</p><p>Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:</p><ul><li><p>The <a href="https://hackage.haskell.org/package/aeson">aeson</a> library provides a <code>Parser</code> type that can be used to parse JSON data into domain types.</p></li><li><p>Likewise, <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> provides a set of parser combinators for parsing command-line arguments.</p></li><li><p>Database libraries like <a href="https://hackage.haskell.org/package/persistent">persistent</a> and <a href="https://hackage.haskell.org/package/postgresql-simple">postgresql-simple</a> have a mechanism for parsing values held in an external data store.</p></li><li><p>The <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.</p></li></ul><p>The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.</p><p>One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the <code>NonEmpty</code> example above: if the parsing and processing logic go out of sync, the program will fail to even compile.</p><h3><a name="the-danger-of-validation"></a>The danger of validation</h3><p>Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?</p><p>Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the <a href="http://langsec.org">language-theoretic security</a> field calls <em>shotgun parsing</em>. In the 2016 paper, <a href="http://langsec.org/papers/langsec-cwes-secdev2016.pdf">The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them</a>, its authors provide the following definition:</p><blockquote><p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.</p></blockquote><p>They go on to explain the problems inherent to such validation techniques:</p><blockquote><p>Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.</p></blockquote><p>In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.</p><p>It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.</p><p>Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p><h2><a name="parsing-not-validating-in-practice"></a>Parsing, not validating, in practice</h2><p>So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”</p><p>My advice: focus on the datatypes.</p><p>Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a <code>Map</code>. Adjust your function’s type signature to accept a <code>Map</code> instead of a list of tuples, and implement it as you normally would.</p><p>Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to <code>Map</code>, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of <code>checkNoDuplicateKeys</code>:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span></code></pre><p>Now the check <em>cannot</em> be omitted, since its result is actually necessary for the program to proceed!</p><p>This hypothetical scenario highlights two simple ideas:</p><ol><li><p><strong>Use a data structure that makes illegal states unrepresentable.</strong> Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.</p></li><li><p><strong>Push the burden of proof upward as far as possible, but no further.</strong> Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.</p></li></ol><p>In other words, write functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!</p><p>Here are a handful of additional points of advice, arranged in no particular order:</p><ul><li><p><strong>Let your datatypes inform your code, don’t let your code control your datatypes.</strong> Avoid the temptation to just stick a <code>Bool</code> in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.</p></li><li><p><strong>Treat functions that return <code>m ()</code> with deep suspicion.</strong> Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.</p></li><li><p><strong>Don’t be afraid to parse data in multiple passes.</strong> Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p></li><li><p><strong>Avoid denormalized representations of data, <em>especially</em> if it’s mutable.</strong> Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.</p><ul><li><p><strong>Keep denormalized representations of data behind abstraction boundaries.</strong> If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.</p></li></ul></li><li><p><strong>Use abstract datatypes to make validators “look like” parsers.</strong> Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract <code>newtype</code> with a smart constructor to “fake” a parser from a validator.</p></li></ul><p>As always, use your best judgement. It probably isn’t worth breaking out <a href="https://hackage.haskell.org/package/singletons">singletons</a> and refactoring your entire application just to get rid of a single <code>error "impossible"</code> call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.</p><h2><a name="recap-reflection-and-related-reading"></a>Recap, reflection, and related reading</h2><p>That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.</p><p>None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about <em>process</em>. My hope is that this is a small step in that direction.</p><p>Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post <a href="https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html">Type Safety Back and Forth</a>. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper <a href="https://kataskeue.com/gdp.pdf">Ghosts of Departed Proofs</a>, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.</p><p>As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit <em>any</em> value. These aren’t “real” values (unlike <code>null</code> in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that <a href="https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf">Fast and Loose Reasoning is Morally Correct</a>. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In fact, <code>Data.List.NonEmpty</code> already provides a <code>head</code> function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Empathy and subjective experience in programming languages2019-10-19T00:00:00Z2019-10-19T00:00:00ZAlexis King<article><p>A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.</p><p>Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?</p><p>I think about that question a lot.</p><h2><a name="2015-called-and-they-want-their-dress-back"></a>2015 called, and they want their dress back</h2><p>Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.</p><p>However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into <em>Star Wars</em>. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.</p><p>Take 2015’s phenomenon of “<a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did <em>this</em>—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.</p><h3><a name="when-something-objective-isn-t"></a>When something objective isn’t</h3><p>Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.</p><p>The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.</p><p>In the case of the dress, whether you <a href="https://en.wikipedia.org/wiki/Yanny_or_Laurel">heard “yanny” or “laurel,”</a> or whether you believe the <em>Sonic</em> games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?</p><p>I think the answer is absolutely, unequivocally <em>yes</em>.</p><h2><a name="subjectivity-in-programming-and-in-programming-languages-specifically"></a>Subjectivity in programming, and in programming languages specifically</h2><p>Quick question: which is better, functional or imperative programming?</p><p>My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?</p><p>Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?</p><p>I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> but I did happen to find a link to <a href="https://news.ycombinator.com/item?id=21282647">a recent discussion</a> on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?</p><p>In a <a href="https://news.ycombinator.com/item?id=21284383">branch of the discussion</a>, one user writes:</p><blockquote><blockquote><p>Haskell is great for business and great in production</p></blockquote><p>I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.</p><p>I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.</p></blockquote><p>The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:</p><blockquote><p>I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.</p><p>[…]</p><p>From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.</p></blockquote><p>Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:</p><blockquote><p>Haskell gives one plenty of rope to hang himself on complexity.</p><p>So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.</p></blockquote><p>Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:</p><blockquote><p>Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.</p><p>[…]</p><p>The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.</p><p>Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.</p></blockquote><p>That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?</p><h3><a name="the-unsatisfying-subjective-reality-of-programming-languages"></a>The unsatisfying subjective reality of programming languages</h3><p>You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?</p><p><em><strong>No!</strong></em></p><p>These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, <em>why?</em> Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.</p><p>While <a href="https://news.ycombinator.com/item?id=21284317">one commenter</a> in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” <a href="https://news.ycombinator.com/item?id=21284540">another</a> says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.</p><p>Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t <em>get it.</em> There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.</p><h3><a name="empathy-and-how-bad-results-come-from-good-intentions"></a>Empathy, and how bad results come from good intentions</h3><p>I’ll admit that these kinds of discussions aren’t <em>always</em> fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.</p><p>Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.</p><p>Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:</p><blockquote><p>Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.</p></blockquote><p>I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?</p><p>If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.</p><p>Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.</p><p>To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:</p><blockquote><p>It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.</p><p>Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.</p></blockquote><p>When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective <strong>without</strong> invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about <em>your own</em> experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.</p><p>It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.</p><p>Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be <em>happier.</em></p><ol class="footnotes"><li id="footnote-1"><p>This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is <em>blah</em>,” but you deserve better. So you, uh, get a <em>meta</em> snarky footnote instead. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Which, to be entirely fair, may well be as subjective as anything else in this blog post. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Demystifying MonadBaseControl2019-09-07T00:00:00Z2019-09-07T00:00:00ZAlexis King<article><p><a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl"><code>MonadBaseControl</code> from the <code>monad-control</code> package</a> is a confusing typeclass, and its methods have complicated types. For many people, it’s nothing more than scary, impossible-to-understand magic that is, for some reason, needed when lifting certain kinds of operations. Few resources exist that adequately explain how, why, and when it works, which sadly seems to have resulted in some <a href="https://en.wikipedia.org/wiki/Fear,_uncertainty,_and_doubt">FUD</a> about its use.</p><p>There’s no doubt that the machinery of <code>MonadBaseControl</code> is complex, and the role it plays in practice is often subtle. However, its essence is actually much simpler than it appears, and I promise it can be understood by mere mortals. In this blog post, I hope to provide a complete survey of <code>MonadBaseControl</code>—how it works, how it’s designed, and how it can go wrong—in a way that is accessible to anyone with a firm grasp of monads and monad transformers. To start, we’ll motivate <code>MonadBaseControl</code> by reinventing it ourselves.</p><h2><a name="the-higher-order-action-problem"></a>The higher-order action problem</h2><p>Say we have a function with the following type:<sup><a href="#footnote-0" id="footnote-ref-0-1">1</a></sup></p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>If we have an action built from a transformer stack like</p><pre><code class="pygments"><span class="nf">bar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span></code></pre><p>then we might wish to apply <code>foo</code> to <code>bar</code>, but that is ill-typed, since <code>IO</code> is not the same as <code>StateT X IO</code>. In cases like these, we often use <code>lift</code>, but it’s not good enough here: <code>lift</code> <em>adds</em> a new monad transformer to an action, but here we need to <em>remove</em> a transformer. So we need a function with a type like this:</p><pre><code class="pygments"><span class="nf">unliftState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span></code></pre><p>However, if you think about that type just a little bit, it’s clear something’s wrong: it throws away information, namely the state. You may remember that a <code>StateT X IO Y</code> action is equivalent to a function of type <code>X -&gt; IO (Y, X)</code>, so our hypothetical <code>unliftState</code> function has two problems:</p><ol><li><p>We have no <code>X</code> to use as the initial state.</p></li><li><p>We’ll lose any modifications <code>bar</code> made to the state, since the result type is just <code>Y</code>, not <code>(Y, X)</code>.</p></li></ol><p>Clearly, we’ll need something more sophisticated, but what?</p><h2><a name="a-na-ve-solution"></a>A naïve solution</h2><p>Given that <code>foo</code> doesn’t know anything about the state, we can’t easily thread it through <code>foo</code> itself. However, by using <code>runStateT</code> explicitly, we could do some of the state management ourselves:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">v</span></code></pre><p>Do you see what’s going on there? It’s not actually very complicated: we get the current state, then pass it as the initial state to <code>runStateT</code>. This produces an action <code>IO (a, s)</code> that has <em>closed over</em> the current state. We can pass that action to <code>foo</code> without issue, since <code>foo</code> is polymorphic in the action’s return type. Finally, all we have to do is <code>put</code> the modified state back into the enclosing <code>StateT</code> computation, and we can get on with our business.</p><p>That strategy works okay when we only have one monad transformer, but it gets hairy quickly as soon as we have two or more. For example, if we had <code>baz :: ExceptT X (StateT Y IO) Z</code>, then we <em>could</em> do the same trick by getting the underlying</p><pre><code class="pygments"><span class="kt">Y</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">Z</span><span class="p">,</span><span class="w"> </span><span class="kt">Y</span><span class="p">)</span></code></pre><p>function, closing over the state, restoring it, and doing the appropriate case analysis to re-raise any <code>ExceptT</code> errors, but that’s a lot of work to do for every single function! What we’d like to do instead is somehow abstract over the pattern we used to write <code>foo'</code> in a way that scales to arbitrary monad transformers.</p><h2><a name="the-essence-of-monadbasecontrol"></a>The essence of <code>MonadBaseControl</code></h2><p>To build a more general solution for “unlifting” arbitrary monad transformers, we need to start thinking about monad transformer state. The technique we used to implement <code>foo'</code> operated on the following process:</p><ol><li><p>Capture the action’s input state and close over it.</p></li><li><p>Package up the action’s output state with its result and run it.</p></li><li><p>Restore the action’s output state into the enclosing transformer.</p></li><li><p>Return the action’s result.</p></li></ol><p>For <code>StateT s</code>, it turns out that the input state and output state are both <code>s</code>, but other monad transformers have state, too. Consider the input and output state for the following common monad transformers:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>StateT s m a</code></td> + <td><code>s -&gt; m (a, s)</code></td> + <td><code>s</code></td> + <td><code>s</code></td> + </tr> + <tr> + <td><code>ReaderT r m a</code></td> + <td><code>r -&gt; m a</code></td> + <td><code>r</code></td> + <td><code>()</code></td> + </tr> + <tr> + <td><code>WriterT w m a</code></td> + <td><code>m (a, w)</code></td> + <td><code>()</code></td> + <td><code>w</code></td> + </tr> + </table> +</div><p>Notice how the input state is whatever is to the left of the <code>-&gt;</code>, while the output state is whatever extra information gets produced alongside the result. Using the same reasoning, we can also deduce the input and output state for compositions of multiple monad transformers, such as the following:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>ReaderT r (WriterT w m) a</code></td> + <td><code>r -&gt; m (a, w)</code></td> + <td><code>r</code></td> + <td><code>w</code></td> + </tr> + <tr> + <td><code>StateT s (ReaderT r m) a</code></td> + <td><code>r -&gt; s -&gt; m (a, s)</code></td> + <td><code>(r, s)</code></td> + <td><code>s</code></td> + </tr> + <tr> + <td><code>WriterT w (StateT s m) a</code></td> + <td><code>s -&gt; m ((a, w), s)</code></td> + <td><code>s</code></td> + <td><code>(w, s)</code></td> + </tr> + </table> +</div><p>Notice that when monad transformers are composed, their states are composed, too. This is useful to keep in mind, since our goal is to capture the four steps above in a typeclass, polymorphic in the state of the monad transformers we need to lift through. At minimum, we need two new operations: one to capture the input state and close over it (step 1) and one to restore the output state (step 3). One class we might come up with could look like this:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>If we can write instances of that typeclass for various transformers, we can use the class’s operations to implement <code>foo'</code> in a generic way that works with any combination of them:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">v</span></code></pre><p>So how do we implement those instances? Let’s start with <code>IO</code>, since that’s the base case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;&amp;&gt;</span><span class="w"> </span><span class="p">(,</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Not very exciting. The <code>StateT s</code> instance, on the other hand, is significantly more interesting:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(,)</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&lt;*&gt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span></code></pre><p><strong>This instance alone includes most of the key ideas behind <code>MonadBaseControl</code>.</strong> There’s a lot going on, so let’s break it down, step by step:</p><ol><li><p>Start by examining the definitions of <code>InputState</code> and <code>OutputState</code>. Are they what you expected? You’d be forgiven for expecting the following:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">s</span> +<span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">s</span></code></pre><p>After all, that’s what we wrote in the table, isn’t it?</p><p>However, if you give it a try, you’ll find it doesn’t work. <code>InputState</code> and <code>OutputState</code> must capture the state of the <em>entire</em> monad, not just a single transformer layer, so we have to combine the <code>StateT s</code> state with the state of the underlying monad. In the simplest case we get</p><pre><code class="pygments"><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>which is boring, but in a more complex case, we need to get something like this:</p><pre><code class="pygments"><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="kt">IO</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="nb">()</span><span class="p">))</span></code></pre><p>Therefore, <code>InputState (StateT s m)</code> combines <code>s</code> with <code>InputState m</code> in a tuple, and <code>OutputState</code> does the same.</p></li><li><p>Moving on, take a look at <code>captureInputState</code> and <code>closeOverInputState</code>. Just as <code>InputState</code> and <code>OutputState</code> capture the state of the entire monad, these functions need to be inductive in the same way.</p><p><code>captureInputState</code> acquires the current state using <code>get</code>, and it combines it with the remaining monadic state using <code>lift captureInputState</code>. <code>closeOverInputState</code> uses the captured state to peel off the outermost <code>StateT</code> layer, then calls <code>closeOverInputState</code> recursively to peel off the rest of them.</p></li><li><p>Finally, <code>restoreOutputState</code> restores the state of the underlying monad stack, then restores the <code>StateT</code> state, ensuring everything ends up back the way it’s supposed to be.</p></li></ol><p>Take the time to digest all that—work through it yourself if you need to—as it’s a dense piece of code. Once you feel comfortable with it, take a look at the instances for <code>ReaderT</code> and <code>WriterT</code> as well:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(,)</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="o">&lt;*&gt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span></code></pre><p>Make sure you understand these instances, too. It should be easier this time, since they share most of their structure with the <code>StateT</code> instance, but note the asymmetry that arises from the differing input and output states. (It may even help to try and write these instances yourself, focusing on the types whenever you get stuck.)</p><p>If you feel alright with them, then congratulations: you’re already well on your way to grokking <code>MonadBaseControl</code>!</p><h3><a name="hiding-the-input-state"></a>Hiding the input state</h3><p>So far, our implementation of <code>MonadBaseControl</code> works, but it’s actually slightly more complicated than it needs to be. As it happens, all valid uses of <code>MonadBaseControl</code> will always end up performing the following pattern:</p><pre><code class="pygments"><span class="nf">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="kr">let</span><span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span></code></pre><p>That is, we close over the input state as soon as we capture it. We can therefore combine <code>captureInputState</code> and <code>closeOverInputState</code> into a single function:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">))</span></code></pre><p>What’s more, we no longer need the <code>InputState</code> associated type at all! This is an improvement, since it simplifies the API and removes the possibility for any misuse of the input state, since it’s never directly exposed. On the other hand, it has a more complicated type: it produces a monadic action <em>that returns another monadic action</em>. This can be a little more difficult to grok, which is why I presented the original version first, but it may help to consider how the above type arises naturally from the following definition:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">captureInputState</span></code></pre><p>Let’s update the <code>MonadBaseControl</code> class to incorporate this simplification:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>We can then update all the instances to use the simpler API by simply fusing the definitions of <code>captureInputState</code> and <code>closeOverInputState</code> together:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">&lt;&amp;&gt;</span><span class="w"> </span><span class="p">(,</span><span class="w"> </span><span class="nb">()</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span></code></pre><p>This is already very close to a full <code>MonadBaseControl</code> implementation. The <code>captureAndCloseOverInputState</code> implementations are getting a little out of hand, but bear with me—they’ll get simpler before this blog post is over.</p><h3><a name="coping-with-partiality"></a>Coping with partiality</h3><p>Our <code>MonadBaseControl</code> class now works with <code>StateT</code>, <code>ReaderT</code>, and <code>WriterT</code>, but one transformer we haven’t considered is <code>ExceptT</code>. Let’s try to extend our table from before with a row for <code>ExceptT</code>:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>ExceptT e m a</code></td> + <td><code>m (Either e a)</code></td> + <td><code>()</code></td> + <td><code>???</code></td> + </tr> + </table> +</div><p>Hmm… what <em>is</em> the output state for <code>ExceptT</code>?</p><p>The answer can’t be <code>e</code>, since we might not end up with an <code>e</code>—the computation might not fail. <code>Maybe e</code> would be closer… could that work?</p><p>Well, let’s try it. Let’s write a <code>MonadBaseControl</code> instance for <code>ExceptT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Sadly, the above implementation doesn’t typecheck; it is rejected with the following type error:</p><pre><code>• Couldn't match type ‘Either e a’ with ‘(a, Maybe e)’ + Expected type: m (b ((a, Maybe e), OutputState m)) + Actual type: m (b (Either e a, OutputState m)) +• In the second argument of ‘($)’, namely + ‘captureAndCloseOverInputState (runExceptT m)’ + In a stmt of a 'do' block: + m' &lt;- lift $ captureAndCloseOverInputState (runExceptT m) + In the expression: + do m' &lt;- lift $ captureAndCloseOverInputState (runExceptT m) + return do ((v, s'), ss') &lt;- m' + pure (v, (s', ss')) +</code></pre><p>We promised a <code>(a, Maybe e)</code>, but we have an <code>Either e a</code>, and there’s certainly no way to get the former from the latter. Are we stuck? (If you’d like, take a moment to think about how you’d solve this type error before moving on, as it may be helpful for understanding the following solution.)</p><p>The fundamental problem here is <em>partiality</em>. The type of the <code>captureAndCloseOverInputState</code> method always produces an action in the base monad that includes an <code>a</code> <em>in addition</em> to some other output state. But <code>ExceptT</code> is different: when it an error is raised, it doesn’t produce an <code>a</code> at all—it only produces an <code>e</code>. Therefore, as written, it’s impossible to give <code>ExceptT</code> a <code>MonadBaseControl</code> instance.</p><p>Of course, we’d very much <em>like</em> to give <code>ExceptT</code> a <code>MonadBaseControl</code> instance, so that isn’t very satisfying. Somehow, we need to change <code>captureAndCloseOverInputState</code> so that it doesn’t always need to produce an <code>a</code>. There are a few ways we could accomplish that, but an elegant way to do it is this:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>We’ve replaced the old <code>OutputState</code> associated type with a new <code>WithOutputState</code> type, and the key difference between them is that <code>WithOutputState</code> describes the type of a <em>combination</em> of the result (of type <code>a</code>) and the output state, rather than describing the type of the output state alone. For total monad transformers like <code>StateT</code>, <code>ReaderT</code>, and <code>WriterT</code>, <code>WithOutputState m a</code> will just be a tuple of the result value and the output state, the same as before. For example, here’s an updated <code>MonadBaseControl</code> instance for <code>StateT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">a</span></code></pre><p>Before we consider how this helps us with <code>ExceptT</code>, let’s pause for a moment and examine the revised <code>StateT</code> instance in detail, as there are some new things going on here:</p><ul><li><p>Take a close look at the definition of <code>WithOutputState (StateT s m) a</code>. Note that we’ve defined it to be <code>WithOutputState m (a, s)</code>, <em>not</em> <code>(WithOutputState m a, s)</code>. Consider, for a moment, the difference between these types. Can you see why we used the former, not the latter?</p><p>If it’s unclear to you, that’s okay—let’s illustrate the difference with an example. Consider two similar monad transformer stacks:</p><pre><code class="pygments"><span class="nf">m1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="n">a</span> +<span class="nf">m2</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="n">a</span></code></pre><p>Both these stacks contain <code>StateT</code> and <code>ExceptT</code>, but they are layered in a different order. What’s the difference? Well, consider what <code>m1</code> and <code>m2</code> return once fully unwrapped:</p><pre><code class="pygments"><span class="nf">runExceptT</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m1</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))</span> +<span class="nf">runStateT</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m2</span><span class="p">)</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span></code></pre><p>These results are meaningfully different: in <code>m1</code>, the state is discarded if an error is raised, but in <code>m2</code>, the final state is always returned, even if the computation is aborted. What does this mean for <code>WithOutputState</code>?</p><p>Here’s the important detail: <strong>the state is discarded when <code>ExceptT</code> is “inside” <code>StateT</code>, not the other way around.</strong> This can be counterintuitive, since the <code>s</code> ends up <em>inside</em> the <code>Either</code> when the <code>StateT</code> constructor is on the <em>outside</em> and vice versa. This is really just a property of how monad transformers compose, not anything specific to <code>MonadBaseControl</code>, so an explanation of why this happens is outside the scope of this blog post, but the relevant insight is that the <code>m</code> in <code>StateT s m a</code> controls the eventual action’s output state.</p><p>If we had defined <code>WithOutputState (StateT s m) a</code> to be <code>(WithOutputState m a, s)</code>, we’d be in a pickle, since <code>m</code> would be unable to influence the presence of <code>s</code> in the output state. Therefore, we have no choice but to use <code>WithOutputState m (a, s)</code>. (If you are still confused by this, try it yourself; you’ll find that there’s no way to make the other definition typecheck.)</p></li><li><p>Now that we’ve developed an intuitive understanding of why <code>WithOutputState</code> must be defined the way it is, let’s look at things from another perspective. Consider the type of <code>runStateT</code> once more:</p><pre><code class="pygments"><span class="nf">runStateT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span></code></pre><p>Note that the result type is <code>m (a, s)</code>, with the <code>m</code> on the outside. As it happens, this correspondence simplifies the definition of <code>captureAndCloseOverInputState</code>, since we no longer have to do any fiddling with its result—it’s already in the proper shape, so we can just return it directly.</p></li><li><p>Finally, this instance illustrates an interesting change to <code>restoreOutputState</code>. Since the <code>a</code> is now packed inside the <code>WithOutputState m a</code> value, the caller of <code>captureAndCloseOverInputState</code> needs some way to get the <code>a</code> back out! Conveniently, <code>restoreOutputState</code> can play that role, both restoring the output state and unpacking the result.</p><p>Even ignoring partial transformers like <code>ExceptT</code>, this is an improvement over the old API, as it conveniently prevents the programmer from forgetting to call <code>restoreOutputState</code>. However, as we’ll see shortly, it is much more than a convenience: once <code>ExceptT</code> comes into play, it is essential!</p></li></ul><p>With those details addressed, let’s return to <code>ExceptT</code>. Using the new interface, writing an instance for <code>ExceptT</code> is not only possible, it’s actually rather easy:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">either</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span></code></pre><p>This instance illustrates why it’s so crucial that <code>restoreOutputState</code> have the aforementioned dual role: it must handle the case where no <code>a</code> exists at all! In the case of <code>ExceptT</code>, it restores the state in the enclosing monad by re-raising an error.</p><p>Now all that’s left to do is update the other instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">a</span></code></pre><p>Finally, we can update our lifted variant of <code>foo</code> to use the new interface so it will work with transformer stacks that include <code>ExceptT</code>:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span></code></pre><p>At this point, it’s worth considering something: although getting the <code>MonadBaseControl</code> class and instances right was a lot of work, the resulting <code>foo'</code> implementation is actually incredibly simple. That’s a good sign, since we only have to write the <code>MonadBaseControl</code> instances once (in a library), but we have to write functions like <code>foo'</code> quite often.</p><h2><a name="scaling-to-the-real-monadbasecontrol"></a>Scaling to the real <code>MonadBaseControl</code></h2><p>The <code>MonadBaseControl</code> class we implemented in the previous section is complete. It is a working, useful class that is equivalent in power to <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl">the “real” <code>MonadBaseControl</code> class in the <code>monad-control</code> library</a>. However, if you compare the two, you’ll notice that the version in <code>monad-control</code> looks a little bit different. What gives?</p><p>Let’s compare the two classes side by side:</p><pre><code class="pygments"><span class="c1">-- ours</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> + +<span class="c1">-- theirs</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Let’s start with the similarities, since those are easy:</p><ul><li><p>Our <code>WithOutputState</code> associated type is precisely equivalent to their <code>StM</code> associated type, they just use a (considerably) shorter name.</p></li><li><p>Likewise, our <code>restoreOutputState</code> method is precisely equivalent to their <code>restoreM</code> method, simply under a different name.</p></li></ul><p>That leaves <code>captureAndCloseOverInputState</code> and <code>liftBaseWith</code>. Those two methods both do similar things, but they aren’t identical, and that’s where all the differences lie. To understand <code>liftBaseWith</code>, let’s start by inlining the definition of the <code>RunInBase</code> type alias so we can see the fully-expanded type:</p><pre><code class="pygments"><span class="nf">liftBaseWith</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">((</span><span class="n">forall</span><span class="w"> </span><span class="n">c</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">c</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>That type is complicated! However, if we break it down, hopefully you’ll find it’s not as scary as it first appears. Let’s reimplement the <code>foo'</code> example from before using <code>liftBaseWith</code> to show how this version of <code>MonadBaseControl</code> works:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>This is, in some ways, superficially similar to the version we wrote using our version of <code>MonadBaseControl</code>. Just like in our version, we capture the input state, apply <code>foo</code> in the <code>IO</code> monad, then restore the state. But what exactly is doing the state capturing, and what is <code>runInBase</code>?</p><p>Let’s start by adding a type annotation to <code>runInBase</code> to help make it a little clearer what’s going on:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">b</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>That type should look sort of recognizable. If we replace <code>StM</code> with <code>WithOutputState</code>, then we get a type that looks very similar to that of our original <code>closeOverInputState</code> function, except it doesn’t need to take the input state as an argument. How does that work?</p><p>Here’s the trick: <code>liftBaseWith</code> starts by capturing the input state, just as before. However, it then builds a function, <code>runInBase</code>, which is like <code>closeOverInputState</code> partially-applied to the input state it captured. It hands that function to us, and we’re free to apply it to <code>m</code>, which produces the <code>IO (StM m a)</code> action we need, and we can now pass that action to <code>foo</code>. The result is returned in the outer monad, and we restore the state using <code>restoreM</code>.</p><h3><a name="sharing-the-input-state"></a>Sharing the input state</h3><p>At first, this might seem needlessly complicated. When we first started, we separated capturing the input state and closing over it into two separate operations (<code>captureInputState</code> and <code>closeOverInputState</code>), but we eventually combined them so that we could keep the input state hidden. Why does <code>monad-control</code> split them back into two operations again?</p><p>As it turns out, when lifting <code>foo</code>, there’s no advantage to the more complicated API of <code>monad-control</code>. In fact, we could implement our <code>captureAndCloseOverInputState</code> operation in terms of <code>liftBaseWith</code>, and we could use that to implement <code>foo'</code> the same way we did before:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> + +<span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span></code></pre><p>However, that approach has a downside once we need to lift more complicated functions. <code>foo</code> is exceptionally simple, as it only accepts a single input argument, but what if we wanted to lift a more complicated function that took <em>two</em> monadic arguments, such as this one:</p><pre><code class="pygments"><span class="nf">bar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>We could implement that by calling <code>captureAndCloseOverInputState</code> twice, like this:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">ma&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">ma</span> +<span class="w"> </span><span class="n">mb&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">mb</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">bar</span><span class="w"> </span><span class="n">ma&#39;</span><span class="w"> </span><span class="n">mb&#39;</span><span class="p">)</span></code></pre><p>However, that would capture the monadic state twice, which is rather inefficient. By using <code>liftBaseWith</code>, the state capturing is done just once, and it’s shared between all calls to <code>runInBase</code>:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>By providing a “running” function (<code>runInBase</code>) instead of direct access to the input state, <code>liftBaseWith</code> allows sharing the captured input state between multiple actions without exposing it directly.</p><h3><a name="sidebar-continuation-passing-and-impredicativity"></a>Sidebar: continuation-passing and impredicativity</h3><p>One last point before we move on: although the above explains why <code>captureAndCloseOverInputState</code> is insufficient, you may be left wondering why <code>liftBaseWith</code> can’t just <em>return</em> <code>runInBase</code>. Why does it need to be given a continuation? After all, it would be nicer if we could just write this:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">askRunInBase</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">))</span></code></pre><p>To understand the problem with a hypothetical <code>askRunInBase</code> function, remember that the type of <code>runInBase</code> is polymorphic:</p><pre><code class="pygments"><span class="nf">runInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This is important, since if you need to lift a function with a type like</p><pre><code class="pygments"><span class="nf">baz</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)</span></code></pre><p>then you’ll want to instantiate that <code>a</code> variable with two different types. We’d need to retain that power in <code>askRunInBase</code>, so it would need to have the following type:</p><pre><code class="pygments"><span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span></code></pre><p>Sadly, that type is illegal in Haskell. Type constructors must be applied to monomorphic types, but in the above type signature, <code>m</code> is applied to a polymorphic type.<sup><a href="#footnote-1" id="footnote-ref-1-1">2</a></sup> The <code>RankNTypes</code> GHC extension introduces a single exception: the <code>(-&gt;)</code> type constructor is special and may be applied to polymorphic types. That’s why <code>liftBaseWith</code> is legal, but <code>askRunInBase</code> is not: since <code>liftBaseWith</code> is passed a higher-order function that receives <code>runInBase</code> as an argument, the polymorphic type appears immediately under an application of <code>(-&gt;)</code>, which is allowed.</p><p>The aforementioned restriction means we’re basically out of luck, but if you <em>really</em> want <code>askRunInBase</code>, there is a workaround. GHC is perfectly alright with a field of a datatype being polymorphic, so we can define a newtype that wraps a suitably-polymorphic function:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span></code></pre><p>We can now alter <code>askRunInBase</code> to return our newtype, and we can implement it in terms of <code>liftBaseWith</code>:<sup><a href="#footnote-2" id="footnote-ref-2-1">3</a></sup></p><pre><code class="pygments"><span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">runInBase</span></code></pre><p>To use <code>askRunInBase</code>, we have to pattern match on the <code>RunInBase</code> constructor, but it isn’t very noisy, since we can do it directly in a <code>do</code> binding. For example, we could implement a lifted version of <code>baz</code> this way:</p><pre><code class="pygments"><span class="nf">baz&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="nf">baz&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">askRunInBase</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">))</span> +<span class="w"> </span><span class="n">bitraverse</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>As of version 1.0.2.3, <code>monad-control</code> does not provide a newtype like <code>RunInBase</code>, so it also doesn’t provide a function like <code>askRunInBase</code>. For now, you’ll have to use <code>liftBaseWith</code>, but it might be a useful future addition to the library.</p><h2><a name="pitfalls"></a>Pitfalls</h2><p>At this point in the blog post, we’ve covered the essentials of <code>MonadBaseControl</code>: how it works, how it’s designed, and how you might go about using it. However, so far, we’ve only considered situations where <code>MonadBaseControl</code> works well, and I’ve intentionally avoided examples where the technique breaks down. In this section, we’re going to take a look at the pitfalls and drawbacks of <code>MonadBaseControl</code>, plus some ways they can be mitigated.</p><h3><a name="no-polymorphism-no-lifting"></a>No polymorphism, no lifting</h3><p>All of the pitfalls of <code>MonadBaseControl</code> stem from the same root problem, and that’s the particular technique it uses to save and restore monadic state. We’ll start by considering one of the simplest ways that technique is thwarted, and that’s monomorphism. Consider the following two functions:</p><pre><code class="pygments"><span class="nf">poly</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span> +<span class="nf">mono</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">X</span></code></pre><p>Even after all we’ve covered, it may surprise you to learn that although <code>poly</code> can be easily lifted to <code>MonadBaseControl IO m =&gt; m a -&gt; m a</code>, it’s <em>impossible</em> to lift <code>mono</code> to <code>MonadBaseControl IO m =&gt; m X -&gt; m X</code>. It’s a little unintuitive, as we often think of polymorphic types as being more complicated (so surely lifting polymorphic functions ought to be harder), but in fact, it’s the flexibility of polymorphism that allows <code>MonadBaseControl</code> to work in the first place.</p><p>To understand the problem, remember that when we lift a function of type <code>forall a. b a -&gt; b a</code> using <code>MonadBaseControl</code>, we actually instantiate <code>a</code> to <code>(StM m c)</code>. That produces a function of type <code>b (StM m c) -&gt; b (StM m c)</code>, which is isomorphic to the <code>m c -&gt; m c</code> type we want. The instantiation step is easily overlooked, but it’s crucial, since otherwise we have no way to thread the state through the otherwise opaque function we’re trying to lift!</p><p>In the case of <code>mono</code>, that’s exactly the problem we’re faced with. <code>mono</code> will not accept an <code>IO (StM m X)</code> as an argument, only precisely an <code>IO X</code>, so we can’t pass along the monadic state. For all its machinery, <code>MonadBaseControl</code> is no help at all if no polymorphism is involved. Trying to generalize <code>mono</code> without modifying its implementation is a lost cause.</p><h3><a name="the-dangers-of-discarded-state"></a>The dangers of discarded state</h3><p>Our inability to lift <code>mono</code> is frustrating, but at least it’s conclusively impossible. In practice, however, many functions lie in an insidious in-between: polymorphic enough to be lifted, but not without compromises. The simplest of these functions have types such as the following:</p><pre><code class="pygments"><span class="nf">sideEffect</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Unlike <code>mono</code>, it’s entirely possible to lift <code>sideEffect</code>:</p><pre><code class="pygments"><span class="nf">sideEffect&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">sideEffect&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">sideEffect</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>This definition typechecks, but you may very well prefer it didn’t, since it has a serious problem: any changes made by <code>m</code> to the monadic state are completely discarded once <code>sideEffect'</code> returns! Since <code>sideEffect'</code> never calls <code>restoreM</code>, there’s no way the state of <code>m</code> can be any different from the original state, but it’s impossible to call <code>restoreM</code> since we don’t actually get an <code>StM m ()</code> result from <code>sideEffect</code>.</p><p>Sometimes this may be acceptable, since some monad transformers don’t actually have any output state anyway, such as <code>ReaderT r</code>. In other cases, however, <code>sideEffect'</code> could be a bug waiting to happen. One way to make <code>sideEffect'</code> safe would be to add a <code>StM m a ~ a</code> constraint to its context, since that guarantees the monad transformers being lifted through are stateless, and nothing is actually being discarded. Of course, that significantly restricts the set of monad transformers that can be lifted through.</p><h4><a name="rewindable-state"></a>Rewindable state</h4><p>One scenario where state discarding can actually be useful is operations with so-called rewindable or transactional state. The most common example of such an operation is <code>catch</code>:</p><pre><code class="pygments"><span class="nf">catch</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Exception</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>When lifted, state changes from the action <em>or</em> from the exception handler will be “committed,” but never both. If an exception is raised during the computation, those state changes are discarded (“rewound”), giving <code>catch</code> a kind of backtracking semantics. This behavior arises naturally from the way a lifted version of <code>catch</code> must be implemented:</p><pre><code class="pygments"><span class="nf">catch&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Exception</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">catch&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">catch</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>If <code>m</code> raises an exception, it will never return an <code>StM m a</code> value, so there’s no way to get ahold of any of the state changes that happened before the exception. Therefore, the only option is to discard that state.</p><p>This behavior is actually quite useful, and it’s definitely not unreasonable. However, useful or not, it’s inconsistent with state changes to mutable values like <code>IORef</code>s or <code>MVar</code>s (they stay modified whether an exception is raised or not), so it can still be a gotcha. Either way, it’s worth being aware of.</p><h4><a name="partially-discarded-state"></a>Partially discarded state</h4><p>The next function we’re going to examine is <code>finally</code>:</p><pre><code class="pygments"><span class="nf">finally</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function has a similar type to <code>catch</code>, and it even has similar semantics. Like <code>catch</code>, <code>finally</code> can be lifted, but unlike <code>catch</code>, its state <em>can’t</em> be given any satisfying treatment. The only way to implement a lifted version is</p><pre><code class="pygments"><span class="nf">finally&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">finally&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">finally</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>which always discards all state changes made by the second argument. This is clear just from looking at <code>finally</code>’s type: since <code>b</code> doesn’t appear anywhere in the return type, there’s simply no way to access that action’s result, and therefore no way to access its modified state.</p><p>However, don’t despair: there actually <em>is</em> a way to produce a lifted version of <code>finally</code> that preserves all state changes. It can’t be done by lifting <code>finally</code> directly, but if we reimplement <code>finally</code> in terms of simpler lifted functions that are more amenable to lifting, we can produce a lifted version of <code>finally</code> that preserves all the state:<sup><a href="#footnote-3" id="footnote-ref-3-1">4</a></sup></p><pre><code class="pygments"><span class="nf">finally&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">finally&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mask&#39;</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">restore</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">try</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="p">(</span><span class="n">restore</span><span class="w"> </span><span class="n">ma</span><span class="p">))</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">throwIO</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SomeException</span><span class="p">))</span> +<span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">&lt;*</span><span class="w"> </span><span class="n">mb</span></code></pre><p>This illustrates an important (and interesting) point about <code>MonadBaseControl</code>: whether or not an operation can be made state-preserving is not a fundamental property of the operation’s type, but rather a property of the types of the exposed primitives. There is sometimes a way to implement a state-preserving variant of operations that might otherwise seem unliftable given the right primitives and a bit of cleverness.</p><h4><a name="forking-state"></a>Forking state</h4><p>As a final example, I want to provide an example where the state may not actually be discarded <em>per se</em>, just inaccessible. Consider the type of <code>forkIO</code>:</p><pre><code class="pygments"><span class="nf">forkIO</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">ThreadId</span></code></pre><p>Although <code>forkIO</code> isn’t actually polymorphic in its argument, we can convert <em>any</em> <code>IO</code> action to one that produces <code>()</code> via <code>void</code>, so it might as well be. Therefore, we can lift <code>forkIO</code> in much the same way we did with <code>sideEffect</code>:</p><pre><code class="pygments"><span class="nf">forkIO&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">ThreadId</span> +<span class="nf">forkIO&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">forkIO</span><span class="w"> </span><span class="p">(</span><span class="n">void</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>As with <code>sideEffect</code>, we can’t recover the output state, but in this case, there’s a fundamental reason that goes deeper than the types: we’ve forked off a concurrent computation! We’ve therefore split the state in two, which might be what we want… but it also might not. <code>forkIO</code> is yet another illustration that it’s important to think about the state-preservation semantics when using <code>MonadBaseControl</code>, or you may end up with a bug!</p><h2><a name="monadbasecontrol-in-context"></a><code>MonadBaseControl</code> in context</h2><p>Congratulations: you’ve made it through most of this blog post. If you’ve followed everything so far, you now understand <code>MonadBaseControl</code>. All the tricky parts are over. However, before wrapping up, I’d like to add a little extra information about how <code>MonadBaseControl</code> relates to various other parts of the Haskell ecosystem. In practice, that information can be as important as understanding <code>MonadBaseControl</code> itself.</p><h3><a name="the-remainder-of-monad-control"></a>The remainder of <code>monad-control</code></h3><p>If you look at <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html">the documentation for <code>monad-control</code></a>, you’ll find that it provides more than just the <code>MonadBaseControl</code> typeclass. I’m not going to cover everything else in detail in this blog post, but I do want to touch upon it briefly.</p><p>First off, you should definitely take a look at the handful of helper functions provided by <code>monad-control</code>, such as <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:control"><code>control</code></a> and <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:liftBaseOp_"><code>liftBaseOp_</code></a>. These functions provide support for lifting common function types without having to use <code>liftBaseWith</code> directly. It’s useful to understand <code>liftBaseWith</code>, since it’s the most general way to use <code>MonadBaseControl</code>, but in practice, it is simpler and more readable to use the more specialized functions wherever possible. Many of the examples in this very blog post could be simplified using them, and I only stuck to <code>liftBaseWith</code> to introduce as few new concepts at a time as possible.</p><p>Second, I’d like to mention the related <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadTransControl"><code>MonadTransControl</code></a> typeclass. You hopefully remember from earlier in the blog post how we defined <code>MonadBaseControl</code> instances inductively so that we could lift all the way down to the base monad. <code>MonadTransControl</code> is like <code>MonadBaseControl</code> if it intentionally did <em>not</em> do that—it allows lifting through a single transformer at a time, rather than through all of them at once.</p><p>Usually, <code>MonadTransControl</code> is not terribly useful to use directly (though I did use it once <a href="/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/#making-mtls-classes-derivable">in a previous blog post of mine</a> to help derive instances of mtl-style classes), but it <em>is</em> useful for implementing <code>MonadBaseControl</code> instances for your own transformers. If you define a <code>MonadTransControl</code> instance for your monad transformer, you can get a <code>MonadBaseControl</code> implementation for free using the provided <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:ComposeSt"><code>ComposeSt</code></a>, <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultLiftBaseWith"><code>defaultLiftBaseWith</code></a>, and <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultRestoreM"><code>defaultRestoreM</code></a> bindings; see the documentation for more details.</p><h3><a name="lifted-base-and-lifted-async"></a><code>lifted-base</code> and <code>lifted-async</code></h3><p>If you’re going to use <code>MonadBaseControl</code>, the <a href="http://hackage.haskell.org/package/lifted-base"><code>lifted-base</code></a> and <a href="http://hackage.haskell.org/package/lifted-async"><code>lifted-async</code></a> packages are good to know about. As their names imply, they provide lifted versions of bindings in the <code>base</code> and <code>async</code> packages, so you can use them directly without needing to lift them yourself. For example, if you needed a lifted version of <code>mask</code> from <code>Control.Exception</code>, you could swap it for the <code>mask</code> export from <code>Control.Exception.Lifted</code>, and everything would mostly just work (though always be sure to check the documentation for any caveats on state discarding).</p><h3><a name="relationship-to-monadunliftio"></a>Relationship to <code>MonadUnliftIO</code></h3><p>Recently, FP Complete has developed the <a href="https://hackage.haskell.org/package/unliftio"><code>unliftio</code></a> package as an alternative to <code>monad-control</code>. It provides the <a href="https://hackage.haskell.org/package/unliftio-core-0.1.2.0/docs/Control-Monad-IO-Unlift.html#t:MonadUnliftIO"><code>MonadUnliftIO</code></a> typeclass, which is similar in spirit to <code>MonadBaseControl</code>, but heavily restricted: it is specialized to <code>IO</code> as the base monad, and it <em>only</em> allows instances for stateless monads, such as <code>ReaderT</code>. This is designed to encourage the so-called <a href="https://www.fpcomplete.com/blog/2017/06/readert-design-pattern"><code>ReaderT</code> design pattern</a>, which avoids ever using stateful monads like <code>ExceptT</code> or <code>StateT</code> over <code>IO</code>, encouraging the use of <code>IO</code> exceptions and mutable variables (e.g. <code>MVar</code>s or <code>TVar</code>s) instead.</p><p>I should be clear: I really like most of what FP Complete has done—to this day, I still use <code>stack</code> as my Haskell build tool of choice—and I think the suggestions given in the aforementioned “<code>ReaderT</code> design pattern” blog post have real weight to them. I have a deep respect for Michael Snoyman’s commitment to opinionated, user-friendly tools and libraries. But truthfully, I can’t stand <code>MonadUnliftIO</code>.</p><p><code>MonadUnliftIO</code> is designed to avoid all the complexity around state discarding that <code>MonadBaseControl</code> introduces, and on its own, that’s a noble goal. Safety first, after all. The problem is that <code>MonadUnliftIO</code> really is extremely limiting, and what’s more, it can actually be trivially encoded in terms of <code>MonadBaseControl</code> as follows:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">MonadUnliftIO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This alias can be used to define safe, lifted functions that never discard state while still allowing functions that <em>can</em> be safely lifted through stateful transformers to do so. Indeed, the <a href="https://hackage.haskell.org/package/lifted-async-0.10.0.4/docs/Control-Concurrent-Async-Lifted-Safe.html"><code>Control.Concurrent.Async.Lifted.Safe</code></a> module from <code>lifted-async</code> does exactly that (albeit with a slightly different formulation than the above alias).</p><p>To be fair, the <code>unliftio</code> README does address this in its <a href="https://github.com/fpco/unliftio/tree/bb2e26e7fbbaebb15555f417ba9753a76b3218b2/unliftio#monad-control">comparison section</a>:</p><blockquote><p><code>monad-control</code> allows us to unlift both styles. In theory, we could write a variant of <code>lifted-base</code> that never does state discards […] In other words, this is an advantage of <code>monad-control</code> over <code>MonadUnliftIO</code>. We've avoided providing any such extra typeclass in this package though, for two reasons:</p><ul><li><p><code>MonadUnliftIO</code> is a simple typeclass, easy to explain. We don't want to complicated [sic] matters […]</p></li><li><p>Having this kind of split would be confusing in user code, when suddenly [certain operations are] not available to us.</p></li></ul></blockquote><p>In other words, the authors of <code>unliftio</code> felt that <code>MonadBaseControl</code> was simply not worth the complexity, and they could get away with <code>MonadUnliftIO</code>. Frankly, if you feel the same way, by all means, use <code>unliftio</code>. I just found it too limiting given the way I write Haskell, plain and simple.</p><h2><a name="recap"></a>Recap</h2><p>So ends another long blog post. As often seems the case, I set out to write something short, but I ended up writing well over 5,000 words. I suppose that means I learned something from this experience, too: <code>MonadBaseControl</code> is more complicated than I had anticipated! Maybe there’s something to take away from that.</p><p>In any case, it’s over now, so I’d like to briefly summarize what we’ve covered:</p><ul><li><p><a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl"><code>MonadBaseControl</code></a> allows us to lift higher-order monadic operations.</p></li><li><p>It operates by capturing the current monadic state and explicitly threading it through the action in the base monad before restoring it.</p></li><li><p>That technique works well for polymorphic operations for the type <code>forall a. b a -&gt; b a</code>, but it can be tricky or even impossible for more complex operations, sometimes leading to discarded state.</p><p>This can sometimes be mitigated by restricting certain operations to stateless monads using a <code>StM m a ~ a</code> constraint, or by reimplementing the operation in terms of simpler primitives.</p></li><li><p>The <a href="http://hackage.haskell.org/package/lifted-base"><code>lifted-base</code></a> and <a href="http://hackage.haskell.org/package/lifted-async"><code>lifted-async</code></a> packages provide lifted versions of existing operations, avoiding the need to lift them yourself.</p></li></ul><p>As with many abstractions in Haskell, don’t worry too much if you don’t have a completely firm grasp of <code>MonadBaseControl</code> at first. Insight often comes with repeated experience, and <code>monad-control</code> can still be used in useful ways even without a perfect understanding. My hope is that this blog post has helped you build intuitions about <code>MonadBaseControl</code> even if some of the underlying machinery remains a little fuzzy, and I hope it can also serve as a reference for those who want or need to understand (or just be reminded of) all the little details.</p><p>Finally, I’ll admit <code>MonadBaseControl</code> isn’t especially elegant or beautiful as Haskell abstractions go. In fact, in many ways, it’s a bit of a kludge! Perhaps, in time, effect systems will evolve and mature so that it and its ilk are no longer necessary, and they may become distant relics of an inferior past. But in the meantime, it’s here, it’s useful, and I think it’s worth embracing. If you’ve shied away from it in the past, I hope I’ve illuminated it enough to make you consider giving it another try.</p><ol class="footnotes"><li id="footnote-0"><p>One example of a function with that type is <code>mask_</code>. <a href="#footnote-ref-0-1">↩</a></p></li><li id="footnote-1"><p>Types with polymorphic types under type constructors are called <em>impredicative</em>. GHC technically has limited support for impredicativity via the <code>ImpredicativeTypes</code> language extension, but as of GHC 8.8, it has been fairly broken for some time. A fix is apparently being worked on, but even if that effort is successful, I don’t know what impact it will have on type inference. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Note that <code>askRunInBase = liftBaseWith (pure . RunInBase)</code> does <em>not</em> typecheck, as it would require impredicative polymorphism: it would require instantiating the type of <code>(.)</code> with polymorphic types. The version using <code>($)</code> works because GHC actually has special typechecking rules for <code>($)</code>! Effectively, <code>f $ x</code> is really syntax in GHC. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Assume that <code>mask'</code> is a suitably lifted version of <code>mask</code> (which can in fact be made state-preserving). <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>An opinionated guide to Haskell in 20182018-02-10T00:00:00Z2018-02-10T00:00:00ZAlexis King<article><p>For me, this month marks the end of an era in my life: as of February 2018, I am no longer employed writing Haskell. It’s been a fascinating two years, and while I am excitedly looking forward to what I’ll be doing next, it’s likely I will continue to write Haskell in my spare time. I’ll probably even write it again professionally in the future.</p><p>In the meantime, in the interest of both sharing with others the small amount of wisdom I’ve gained and preserving it for my future self, I’ve decided to write a long, rather dry overview of a few select parts of the Haskell workflow I developed and the ecosystem I settled into. This guide is, as the title notes, <em>opinionated</em>—it is what I used in my day-to-day work, nothing more—and I don’t claim that anything here is the only way to write Haskell, nor even the best way. It is merely what I found helpful and productive. Take from it as much or as little as you’d like.</p><h2><a name="build-tools-and-how-to-use-them"></a>Build tools and how to use them</h2><p>When it comes to building Haskell, you have options. And frankly, most of them are pretty good. There was a time when <code>cabal-install</code> had a (warranted) reputation for being nearly impossible to use and regularly creating dependency hell, but I don’t think that’s the case anymore (though you <em>do</em> need to be a little careful about how you use it). Sandboxed builds work alright, and <code>cabal new-build</code> and the other <code>cabal new-*</code> commands are even better. That said, the UX of <code>cabal-install</code> is still less-than-stellar, and it has sharp edges, especially for someone coming from an ecosystem without a heavyweight compilation process like JavaScript, Ruby, or Python.</p><p>Nix is an alternative way to manage Haskell dependencies, and it seems pretty cool. It has a reputation for being large and complicated, and that reputation does not seem especially unfair, but you get lots of benefits if you’re willing to pay the cost. Unfortunately, I have never used it (though I’ve read a lot about it), so I can’t comment much on it here. Perhaps I’ll try to go all-in with Nix when I purchase my next computer, but for now, my workflow works well enough that I don’t feel compelled to switch.</p><p>Personally, I use <code>stack</code> as my Haskell build tool. It’s easy to use, it works out of the box, and while it doesn’t enjoy the same amount of caching as <code>cabal new-build</code> or Nix, it caches most packages, and it also makes things like git-hosted sources incredibly easy, which (as far as I can tell) can’t be done with <code>cabal-install</code> alone.</p><p>This section is going to be a guide on how <em>I</em> use <code>stack</code>. If you use <code>cabal-install</code> with or without Nix, great! Those tools seem good, too. This is not an endorsement of <code>stack</code> over the other build tools, just a description of how I use it, the issues I ran into, and my solutions to them.</p><h3><a name="understanding-stack-s-model-and-avoiding-its-biggest-gotcha"></a>Understanding <code>stack</code>’s model and avoiding its biggest gotcha</h3><p>Before using <code>stack</code>, there are a few things every programmer should know:</p><ul><li><p><code>stack</code> is not a package manager, it is a build tool. It does not manage a set of “installed” packages; it simply builds targets and their dependencies.</p></li><li><p>The command to build a target is <code>stack build &lt;target&gt;</code>. Just using <code>stack build</code> on its own will build the current project’s targets.</p></li><li><p><strong>You almost certainly do not want to use <code>stack install</code>.</strong></p></li></ul><p>This is the biggest point of confusion I see among new users of <code>stack</code>. After all, when you want to install a package with <code>npm</code>, you type <code>npm install &lt;package&gt;</code>. So a new Haskeller decides to install <code>lens</code>, types <code>stack install lens</code>, and then later tries <code>stack uninstall lens</code>, only to discover that no such command exists. What happened?</p><p><code>stack install</code> is not like <code>npm install</code>. <code>stack install</code> is like <code>make install</code>. It is nothing more than an alias for <code>stack build --copy-bins</code>, and <em>all</em> it does is build the target and copy all of its executables into some relatively global location like <code>~/.local/bin</code>. This is usually not what you want.</p><p>This design decision is not unique to <code>stack</code>; <code>cabal-install</code> suffers from it as well. One can argue that it isn’t unintuitive because it really is just following what <code>make install</code> conventionally does, and the fact that it happens to conflict with things like <code>npm install</code> or even <code>apt-get install</code> is just a naming clash. I think that argument is a poor one, however, and I think the decision to even include a <code>stack install</code> command was a bad idea.</p><p>So, remember: don’t use <code>stack install</code>! <code>stack</code> works best when everything lives inside the current project’s <em>local</em> sandbox, and <code>stack install</code> copies executables into a <em>global</em> location by design. While it might sometimes appear to work, it’s almost always wrong. The <em>only</em> situation in which <code>stack install</code> is the right answer is when you want to install an executable for a use unrelated to Haskell development (that is, something like <code>pandoc</code>) that just so happens to be provided by a Haskell package. <strong>This means no running <code>stack install ghc-mod</code> or <code>stack install intero</code> either, no matter what READMEs might tell you!</strong> Don’t worry: I’ll cover the proper way to install those things later.</p><h3><a name="actually-building-your-project-with-stack"></a>Actually building your project with <code>stack</code></h3><p>Okay, so now that you know to never use <code>stack install</code>, what <em>do</em> you use? Well, <code>stack build</code> is probably all you need. Let’s cover some variations of <code>stack build</code> that I use most frequently.</p><p>Once you have a <code>stack</code> project, you can build it by simply running <code>stack build</code> within the project directory. However, for local development, this is usually unnecessarily slow because it runs the GHC optimizer. For faster development build times, pass the <code>--fast</code> flag to disable optimizations:</p><pre><code>$ stack build --fast +</code></pre><p>By default, <code>stack</code> builds dependencies with coarse-grained, package-level parallelism, but you can enable more fine-grained, module-level parallel builds by adding <code>--ghc-options=-j</code>. Unfortunately, there are conflicting accounts on whether or not this actually makes things faster or slower in practice, and I haven’t extensively tested to see whether or not this is the case, so I mostly leave it off.</p><p>Usually, you also want to build and run the tests along with your code, which you can enable with the <code>--test</code> flag. Additionally, <code>stack test</code> is an alias for <code>stack build --test</code>, so these two commands are equivalent:</p><pre><code>$ stack build --fast --test +$ stack test --fast +</code></pre><p>Also, it is useful to build documentation as well as code! You can do this by passing the <code>--haddock</code> flag, but unfortunately, I find Haddock sometimes takes an unreasonably long time to run. Therefore, since I usually only care about running Haddock on my dependencies, I usually pass the <code>--haddock-deps</code> flag instead, which prevents having to re-run Haddock every time you build:</p><pre><code>$ stack test --fast --haddock-deps +</code></pre><p>Finally, I usually want to build and test my project in the background whenever my code changes. Fortunately, this can be done easily by using the <code>--file-watch</code> flag, making it easy to incrementally change project code and immediately see results:</p><pre><code>$ stack test --fast --haddock-deps --file-watch +</code></pre><p>This is the command I usually use to develop my Haskell projects.</p><h3><a name="accessing-local-documentation"></a>Accessing local documentation</h3><p>While Haskell does not always excel on the documentation front, a small amount of documentation is almost always better than no documentation at all, and I find my dependencies’ documentation to be an invaluable resource while developing. I find many people just look at docs on Hackage or use the hosted instance of Hoogle, but this sometimes leads people astray: they might end up looking at the wrong version of the documentation! Fortunately, there’s an easy solution to this problem, which is to browse the documentation <code>stack</code> installs locally, which is guaranteed to match the version you are using in your current project.</p><p>The easiest way to open local documentation for a particular package is to use the <code>stack haddock --open</code> command. For example, to open the documentation for <code>lens</code>, you could use the following command:</p><pre><code>$ stack haddock --open lens +</code></pre><p>This will open the local documentation in your web browser, and you can browse it at your leisure. If you have already built the documentation using the <code>--haddock-deps</code> option I recommended in the previous section, this command should complete almost instantly, but if you haven’t built the documentation yet, you’ll have to wait as <code>stack</code> builds it for you on-demand.</p><p>While this is a good start, it isn’t perfect. Ideally, I want to have <em>searchable</em> documentation, and fortunately, this is possible to do by running Hoogle locally. This is easy enough with modern versions of <code>stack</code>, which have built-in Hoogle integration, but it still requires a little bit of per-project setup, since you need to build the Hoogle search index with the following command:</p><pre><code>$ stack hoogle -- generate --local +</code></pre><p>This will install Hoogle into the current project if it isn’t already installed, and it will index your dependencies’ documentation and generate a new Hoogle database. Once you’ve done that, you can start a web server that serves a local Hoogle search page with the following command:</p><pre><code>$ stack hoogle -- server --local --port=8080 +</code></pre><p>Navigate to <code>http://localhost:8080</code> in your web browser, and you’ll have a fully-searchable index of all your Haskell packages’ documentation. Isn’t that neat?</p><p>Unfortunately, you <em>will</em> have to manually regenerate the Hoogle database when you install new packages and their documentation, which you can do by re-running <code>stack hoogle -- generate --local</code>. Fortunately, regenerating the database doesn’t take very long, as long as you’ve been properly rebuilding the documentation with <code>--haddock-deps</code>.</p><h3><a name="configuring-your-project"></a>Configuring your project</h3><p>Every project built with <code>stack</code> is configured with two separate files:</p><ul><li><p>The <code>stack.yaml</code> file, which controls which packages are built and what versions to pin your dependencies to.</p></li><li><p>The <code>&lt;project&gt;.cabal</code> file <em>or</em> <code>package.yaml</code> file, which specifies build targets, their dependencies, and which GHC options to apply, among other things.</p></li></ul><p>The <code>.cabal</code> file is, ultimately, what is used to build your project, but modern versions of <code>stack</code> generate projects that use hpack, which uses an alternate configuration file, the <code>package.yaml</code> file, to generate the <code>.cabal</code> file. This can get a little bit confusing, since it means you have <em>three</em> configuration files in your project, one of which is generated from the other one.</p><p>I happen to use and like hpack, so I use a <code>package.yaml</code> file and allow hpack to generate the <code>.cabal</code> file. I have no real love for YAML, and in fact I think custom configuration formats are completely fine, but the primary advantage of hpack is the ability to specify things like GHC options and default language extensions for all targets at once, instead of needing to duplicate them per-target.</p><p>You can think of the <code>.cabal</code> or <code>package.yaml</code> file as a specification for <em>how</em> your project is built and <em>what packages</em> it depends on, but the <code>stack.yaml</code> file is a specification of precisely <em>which version</em> of each package should be used and where it should be fetched from. Also, each <code>.cabal</code> file corresponds to precisely <em>one</em> Haskell package (though it may have any number of executable targets), but a <code>stack.yaml</code> file can specify multiple different packages to build, useful for multi-project builds that share a common library. The details here can be a little confusing, more than I am likely going to be able to explain in this blog post, but for the most part, you can get away with the defaults unless you’re doing something fancy.</p><h3><a name="setting-up-editor-integration"></a>Setting up editor integration</h3><p>Currently, I use Atom to write Haskell. Atom is not a perfect editor by any means, and it leaves a lot to be desired, but it’s easy to set up, and the Haskell editor integration is decent.</p><p>Atom’s editor integration is powered by <code>ghc-mod</code>, a program that uses the GHC API to provide tools to inspect Haskell programs. Installing <code>ghc-mod</code> must be done manually so that Atom’s <code>haskell-ghc-mod</code> package can find it, and this is where a lot of people get tripped up. They run <code>stack install ghc-mod</code>, it installs <code>ghc-mod</code> into <code>~/.local/bin</code>, they put that in their <code>PATH</code>, and things work! …except when a new version of GHC is released a few months later, everything stops working.</p><p>As mentioned above, <strong><code>stack install</code> is not what you want</strong>. Tools like <code>ghc-mod</code>, <code>hlint</code>, <code>hoogle</code>, <code>weeder</code>, and <code>intero</code> work best when installed as part of the sandbox, <em>not</em> globally, since that ensures they will match the current GHC version your project is using. This can be done per-project using the ordinary <code>stack build</code> command, so the easiest way to properly install <code>ghc-mod</code> into a <code>stack</code> project is with the following command:</p><pre><code>$ stack build ghc-mod +</code></pre><p>Unfortunately, this means you will need to run that command inside every single <code>stack</code> project individually in order to properly set it up so that <code>stack exec -- ghc-mod</code> will find the correct executable. One way to circumvent this is by using a recently-added <code>stack</code> flag designed for this explicit purpose, <code>--copy-compiler-tool</code>. This is like <code>--copy-bins</code>, but it copies the executables into a <em>compiler-specific location</em>, so a tool built for GHC 8.0.2 will be stored separately from the same tool built for GHC 8.2.2. <code>stack exec</code> arranges for the executables for the current compiler version to end up in the <code>PATH</code>, so you only need to build and install your tools once per compiler version.</p><p>Does this kind of suck? Yes, a little bit, but it sucks a whole lot less than all your editor integration breaking every time you switch to a project that uses a different version of GHC. I use the following command in a fresh sandbox when a Stackage LTS comes out for a new version of GHC:</p><pre><code>$ stack build --copy-compiler-tool ghc-mod hoogle weeder +</code></pre><p>This way, I only have to build those tools once, and I don’t worry about rebuilding them again until a the next release of GHC. To verify that things are working properly, you should be able to create a fresh <code>stack</code> project, run a command like this one, and get a similar result:</p><pre><code>$ stack exec -- which ghc-mod +/Users/alexis/.stack/compiler-tools/x86_64-osx/ghc-8.2.2/bin/ghc-mod +</code></pre><p>Note that this path is scoped to my operating system and my compiler version, but nothing else—no LTS or anything like that.</p><h2><a name="warning-flags-for-a-safe-build"></a>Warning flags for a safe build</h2><p>Haskell is a relatively strict language as programming languages go, but in my experience, it isn’t quite strict enough. Many things are not errors that probably ought to be, like orphan instances and inexhaustive pattern matches. Fortunately, GHC provides <em>warnings</em> that catch these problems statically, which fill in the gaps. I recommend using the following flags on all projects to ensure everything is caught:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat"><code>-Wcompat</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates"><code>-Wincomplete-record-updates</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints"><code>-Wredundant-constraints</code></a></p></li></ul><p>The <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> option turns on <em>most</em> warnings, but (ironically) not all of them. The <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Weverything"><code>-Weverything</code></a> flag truly turns on <em>all</em> warnings, but some of the warnings left disabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> really are quite silly, like warning when type signatures on polymorphic local bindings are omitted. Some of them, however, are legitimately useful, so I recommend turning them on explicitly.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat"><code>-Wcompat</code></a> enables warnings that make your code more robust in the face of future backwards-incompatible changes. These warnings are trivial to fix and serve as free future-proofing, so I see no reason not to turn these warnings on.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates"><code>-Wincomplete-record-updates</code></a> and <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a> are things I think ought to be enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> because they both catch what are essentially partial pattern-matches (and therefore runtime errors waiting to happen). The fact that <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a> <em>isn’t</em> enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> is so surprising that it can lead to bugs being overlooked, since the extremely similar <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-patterns"><code>-Wincomplete-patterns</code></a> <em>is</em> enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a>.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints"><code>-Wredundant-constraints</code></a> is a useful warning that helps to eliminate unnecessary typeclass constraints on functions, which can sometimes occur if a constraint was previously necessary but ends up becoming redundant due to a change in the function’s behavior.</p><p>I put all five of these flags in the <code>.cabal</code> file (or <code>package.yaml</code>), which enables them everywhere, but this alone is unlikely to enforce a warning-free codebase, since the build will still succeed even in the presence of warnings. Therefore, when building projects in CI, I pass the <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Werror"><code>-Werror</code></a> flag (using <code>--ghc-options=-Werror</code> for <code>stack</code>), which treats warnings as errors and halts the build if any warnings are found. This is useful, since it means warnings don’t halt the whole build while developing, making it possible to write some code that has warnings and still run the test suite, but it still enforces that pushed code be warning-free.</p><h2><a name="any-flavor-you-like"></a>Any flavor you like</h2><p>Haskell is both a language and a spectrum of languages. It is both a standard and a specific implementation. Haskell 98 and Haskell 2010 are good, small languages, and there are a few different implementations, but when people talk about “Haskell”, unqualified, they’re almost always talking about GHC.</p><p>GHC Haskell, in stark contrast to standard Haskell, is neither small nor particularly specific, since GHC ships with <em>dozens</em> of knobs and switches that can be used to configure the language. In theory, this is a little terrifying. How could anyone ever hope to talk about Haskell and agree upon how to write it if there are so many <em>different</em> Haskells, each a little bit distinct? Having a cohesive ecosystem would be completely hopeless.</p><p>Fortunately, in practice, this is not nearly as bad as it seems. The majority of GHC extensions are simple switches: a feature is either on or it is off. Turning a feature on rarely affects code that does not use it, so most extensions can be turned on by default, and programmers may simply avoid the features they do not wish to use, just as any programmer in any programming language likely picks a subset of their language’s features to use on a daily basis. Writing Haskell is not different in this regard, only in the sense that it does not allow all features to be used by default; everything from minor syntactic tweaks to entirely new facets of the type system are opt-in.</p><p>Frankly, I think the UX around this is terrible. I recognize the desire to implement a standard Haskell, and the old <code>-fglasgow-exts</code> was not an especially elegant solution for people wishing to use nonstandard Haskell, but having to insert <code>LANGUAGE</code> pragmas at the top of every module just to take advantage of the best features GHC has to offer is a burden, and it is unnecessarily intimidating. I think much of the Haskell community finds the use of <code>LANGUAGE</code> pragmas preferable to enabling extensions globally using the <code>default-extensions</code> list in the <code>.cabal</code> file, but I cut across the grain on that issue <em>hard</em>. The vast majority of language extensions I use are extensions I want enabled all the time; a list of them at the top of a module is just distracting noise, and it only serves to bury the extensions I really do want to enable on a module-by-module basis. It also makes it tricky to communicate with a team which extensions are acceptable (or even preferable) and which are discouraged.</p><p>My <em><strong>strong</strong></em> recommendation if you decide to write GHC Haskell on a team is to agree as a group to a list of extensions the team is happy with enabling everywhere and putting those extensions in the <code>default-extensions</code> list in the <code>.cabal</code> file. This eliminates clutter, busywork, and the conceptual overhead of remembering which extensions are in favor, and which are discouraged. This is a net win, and it isn’t at all difficult to look in the <code>.cabal</code> file when you want to know which extensions are in use.</p><p>Now, with that small digression out of the way, the question becomes precisely which extensions should go into that <code>default-extensions</code> list. I happen to like using most of the features GHC makes available, so I enable a whopping <strong>34</strong> language extensions <em>by default</em>. As of GHC 8.2, here is my list:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable"><code>DeriveFoldable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor"><code>DeriveFunctor</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric"><code>DeriveGeneric</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift"><code>DeriveLift</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable"><code>DeriveTraversable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a></p></li></ul><p>This is a lot, and a few of them are likely to be more controversial than others. Since I do not imagine everyone will agree with everything in this list, I’ve broken it down into smaller chunks, arranged from what I think ought to be least controversial to most controversial, along with a little bit of justification why each extension is in each category. If you’re interested in coming up with your own list of extensions, the rest of this section is for you.</p><h3><a name="trivial-lifting-of-standards-imposed-limitations"></a>Trivial lifting of standards-imposed limitations</h3><p>A few extensions are tiny changes that lift limitations that really have no reason to exist, other than that they are mandated by the standard. I am not sure why these restrictions are in the standard to begin with, other than perhaps a misguided attempt at making the language simpler. These extensions include the following:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a></p></li></ul><p>These extensions have no business <em>not</em> being turned on everywhere. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a> end up being turned on in almost any nontrivial Haskell module, since without them, the typeclass system is pointlessly and artificially limited.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a> is extremely useful, completely safe, and has zero downsides.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a> are almost impossible to avoid, given how many libraries use them, and they are a completely obvious generalization of single-parameter typeclasses. Much like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a>, I see no real reason to ever leave these disabled.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a> is even stranger to me, since <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyDataDecls"><code>EmptyDataDecls</code></a> is in Haskell 2010, so it’s possible to define empty datatypes in standard Haskell but not exhaustively pattern-match on them! This is silly, and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a> should be standard Haskell.</p><h3><a name="syntactic-conveniences"></a>Syntactic conveniences</h3><p>A few GHC extensions are little more than trivial, syntactic abbreviations. These things would be tiny macros in a Lisp, but they need to be extensions to the compiler in Haskell:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a></p></li></ul><p>All of these extensions are only triggered by explicit use of new syntax, so existing programs will never change behavior when these extensions are introduced.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a> only saves a few characters, but it eliminates the need to come up with a fresh, unique variable name that will only be used once, which is sometimes hard to do and leads to worse names overall. Sometimes, it really is better to leave something unnamed.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a> isn’t something I find I commonly need, but when I do, it’s nice to have. It’s far easier to read than nested <code>if...then...else</code> chains, and it uses the existing guard syntax already used with function declarations and <code>case...of</code>, so it’s easy to understand, even to those unfamiliar with the extension.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a> avoids headaches and clutter when using Haskell records without the <a href="https://www.reddit.com/r/haskell/comments/6jaa5f/recordwildcards_and_binary_parsing/djd5ugj/">accidental identifier capture issues</a> of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards"><code>RecordWildCards</code></a>. It’s a nice, safe compromise that brings some of the benefits of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards"><code>RecordWildCards</code></a> without any downsides.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a> is a logical generalization of tuple syntax in the same vein as standard operator sections, and it’s quite useful when using applicative notation. I don’t see any reason to not enable it.</p><h3><a name="extensions-to-the-deriving-mechanism"></a>Extensions to the deriving mechanism</h3><p>GHC’s typeclass deriving mechanism is one of the things that makes Haskell so pleasant to write, and in fact I think Haskell would be nearly unpalatable to write without it. Boilerplate generation is a good thing, since it defines operations in terms of a single source of truth, and generated code is code you do not need to maintain. There is rarely any reason to write a typeclass instance by hand when the deriving mechanism will write it automatically.</p><p>These extensions give GHC’s typeclass deriving mechanism more power without any cost. Therefore, I see no reason <em>not</em> to enable them:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable"><code>DeriveFoldable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor"><code>DeriveFunctor</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric"><code>DeriveGeneric</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift"><code>DeriveLift</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable"><code>DeriveTraversable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a></p></li></ul><p>The first five of these simply extend the list of typeclasses GHC knows how to derive, something that will only ever be triggered if the user explicitly requests GHC derive one of those classes. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a> is quite possibly one of the most important extensions in all of Haskell, since it dramatically improves <code>newtype</code>s’ utility. Wrapper types can inherit instances they need without any boilerplate, and making increased type safety easier and more accessible is always a good thing in my book.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a> is new to GHC 8.2, but it finally presents the functionality of GHC’s <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> extension in a useful way. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> is useful when used with certain libraries that use <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a> (discussed later) with <code>GHC.Generics</code> to derive instances of classes without the deriving being baked into GHC. Unfortunately, enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> essentially disables the far more useful <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a>, so I do <em>not</em> recommend enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a>. Fortunately, with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a>, it’s possible to opt into the <code>anyclass</code> deriving strategy on a case-by-case basis, getting some nice boilerplate reduction in the process.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a> is useful when GHC’s deriving algorithms aren’t <em>quite</em> clever enough to deduce the instance context automatically, so it allows specifying it manually. This is only useful in a few small situations, but it’s nice to have, and there are no downsides to enabling it, so it ought to be turned on.</p><h3><a name="lightweight-syntactic-adjustments"></a>Lightweight syntactic adjustments</h3><p>A couple extensions tweak Haskell’s syntax in more substantial ways than things like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a>, but not in a significant enough way for them to really be at all surprising:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a> mirror strictness annotations on datatypes, so they are unlikely to be confusing, and they provide a much more pleasant notation for annotating the strictness of bindings than explicit uses of <code>seq</code>.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a> are also fairly self-explanatory: they’re just like type annotations, but for types instead of values. Writing kind signatures explicitly is usually unnecessary, but they can be helpful for clarity or for annotating phantom types when <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPolyKinds"><code>PolyKinds</code></a> is not enabled. Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a> doesn’t have any adverse effects, so I see no reason not to enable it everywhere.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a> adjusts the syntax of types slightly, allowing operators to be used as type constructors and written infix, which is technically backwards-incompatible, but I’m a little suspicious of anyone using <code>(!@#$)</code> as a type variable (especially since standard Haskell does not allow them to be written infix). This extension is useful with some libraries like <code>natural-transformations</code> that provide infix type constructors, and it makes the type language more consistent with the value language.</p><h3><a name="polymorphic-string-literals"></a>Polymorphic string literals</h3><p>I’m putting this extension in a category all of its own, mostly because I don’t think any other Haskell extensions have quite the same set of tradeoffs:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a></p></li></ul><p>For me, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is not optional. Haskell’s infamous “string problem” (discussed in more detail at the end of this blog post) means that <code>String</code> is a linked list of characters, and all code that cares about performance actually uses <code>Text</code>. Manually invoking <code>pack</code> on every single string literal in a program is just noise, and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> solves that noise.</p><p>That said, I actually find I don’t use the polymorphism of string literals very often, and I’d be alright with monomorphic literals if I could make them <em>all</em> have type <code>Text</code>. Unfortunately, there isn’t a way to do this, so <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is the next best thing, even if it sometimes causes some unnecessary ambiguities that require type annotations to resolve.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is an extension that I use so frequently, in so many modules (especially in my test suites) that I would rather keep it on everywhere so I don’t have to care about whether or not it’s enabled in the module I’m currently writing. On the other hand, it certainly isn’t my favorite language extension, either. I wouldn’t go as far as to call it a necessary evil, since I don’t think it’s truly “evil”, but it does seem to be necessary.</p><h3><a name="simple-extensions-to-aid-type-annotation"></a>Simple extensions to aid type annotation</h3><p>The following two extensions significantly round out Haskell’s language for referring to types, making it much easier to insert type annotations where necessary (for removing ambiguity or for debugging type errors):</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a></p></li></ul><p>That the behavior of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a> is <em>not</em> the default is actually one of the most common gotchas for new Haskellers. Sadly, it can theoretically adjust the behavior of existing Haskell programs, so I cannot include it in the list of trivial changes, but I would argue such programs were probably confusing to begin with, and I have never seen a program in practice that was impacted by that problem. I think leaving <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a> off is much, much more likely to be confusing than turning it on.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a> is largely unrelated, but I include it in this category because it’s quite useful and cooperates well with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a>. Use of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a> makes instantiation much more lightweight than full-blown type annotations, and once again, it has no downsides if it is enabled and unused (since it is a syntactic addition). I recommend enabling it.</p><h3><a name="simple-extensions-to-the-haskell-type-system"></a>Simple extensions to the Haskell type system</h3><p>A few extensions tweak the Haskell type system in ways that I think are simple enough to be self-explanatory, even to people who might not have known they existed. These are as follows:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a> is largely just used to define typeclass aliases, which is both useful and self-explanatory. Unifying the type and constraint language also has the effect of allowing type-level programming with constraints, which is sometimes useful, but far rarer in practice than the aforementioned use case.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a> are uncommon, looking at the average type in a Haskell program, but they’re certainly nice to have when you need them. The idea of pushing <code>forall</code>s further into a type to adjust how variables are quantified is something that I find people find fairly intuitive, especially after seeing them used once or twice, and higher-rank types do crop up regularly, if infrequently.</p><h3><a name="intermediate-syntactic-adjustments"></a>Intermediate syntactic adjustments</h3><p>Three syntactic extensions to Haskell are a little bit more advanced than the ones I’ve already covered, and none of them are especially related:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is, on the surface, simple. It changes <code>do</code> notation to use <code>Applicative</code> operations where possible, which allows using <code>do</code> notation with applicative functors that are not monads, and it also makes operations potentially more performant when <code>(&lt;*&gt;)</code> can be implemented more efficiently than <code>(&gt;&gt;=)</code>. In theory, it sounds like there are no downsides to enabling this everywhere. However, there are are a few drawbacks that lead me to put it so low on this list:</p><ol><li><p>It considerably complicates the desugaring of <code>do</code> blocks, to the point where the algorithm cannot even be easily syntactically documented. In fact, an additional compiler flag, <code>-foptimal-applicative-do</code>, is a way to <em>opt into</em> optimal solutions for <code>do</code> block expansions, tweaking the desugaring algorithm to have an <em>O</em>(<em>n</em><sup>3</sup>) time complexity! This means that the default behavior is guided by a heuristic, and desugaring isn’t even especially predictable. This isn’t necessarily so bad, since it’s really only intended as an optimization when some <code>Monad</code> operations are still necessary, but it does dramatically increase the complexity of one of Haskell’s core forms.</p></li><li><p>The desugaring, despite being <em>O</em>(<em>n</em><sup>2</sup>) by default, isn’t even especially clever. It relies on a rather disgusting hack that recognizes <code>return e</code>, <code>return $ e</code>, <code>pure e</code>, or <code>pure $ e</code> expressions <em>syntactically</em>, and it completely gives up if an expression with precisely that shape is not the final statement in a <code>do</code> block. This is a bit awkward, since it effectively turns <code>return</code> and <code>pure</code> into syntax when before they were merely functions, but that isn’t all. It also means that the following <code>do</code> block is <em>not</em> desugared using <code>Applicative</code> operations:</p><pre><code class="pygments"><span class="kr">do</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">t</span> +<span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="n">z</span></code></pre><p>This will use the normal, monadic desugaring, despite the fact that it is trivially desugared into <code>Applicative</code> operations as <code>foo a b *&gt; bar s t *&gt; baz y z</code>. In order to get <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> to trigger here, the <code>do</code> block must be contorted into the following:</p><pre><code class="pygments"><span class="kr">do</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">t</span> +<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="n">z</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">r</span></code></pre><p>This seems like an odd oversight.</p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> doesn’t seem able to cope with <code>do</code> blocks when <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is enabled. I reported this as <a href="https://ghc.haskell.org/trac/ghc/ticket/14471">an issue on the GHC bug tracker</a>, but it hasn’t received any attention, so it’s not likely to get fixed unless someone takes the initiative to do so.</p></li><li><p>Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> can cause problems with code that may have assumed <code>do</code> would always be monadic, and sometimes, that can cause code that typechecks to lead to an infinite loop at runtime. Specifically, if <code>do</code> notation is used to define <code>(&lt;*&gt;)</code> in terms of <code>(&gt;&gt;=)</code>, enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> will cause the definition of <code>(&lt;*&gt;)</code> to become self-referential and therefore divergent. Fortunately, this issue can be easily mitigated by simply writing <code>(&lt;*&gt;) = ap</code> instead, which is clearer and shorter than the equivalent code using <code>do</code>.</p></li></ol><p>Given all these things, it seems <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is a little too new in a few places, and it isn’t quite baked. Still, I keep it enabled by default. Why? Well, <em>usually</em> it works fine without any problems, and when I run into issues, I can disable it on a per-module basis by writing <code>{-# LANGUAGE NoApplicativeDo #-}</code>. I still find that keeping it enabled by default is fine the vast majority of the time, I just sometimes need to work around the bugs.</p><p>In contrast, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a> isn’t buggy at all, as far as I can tell, it’s just not usually useful without fairly advanced features like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> (for type equalities) or <code>GHC.Generics</code>. I mostly use it for <a href="/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/">making lifting instances for <code>mtl</code>-style typeclasses easier to write</a>, which I’ve found to be a tiny bit tricky to explain (mostly due to the use of type equalities in the context), but it works well. I don’t see any real reason to leave this disabled, but if you don’t think you’re going to use it anyway, it doesn’t really matter one way or the other.</p><p>Finally, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a> allow users to extend the pattern language just as they are allowed to extend the value language. Bidirectional pattern synonyms are isomorphisms, and it’s quite useful to allow those isomorphisms to be used with Haskell’s usual pattern-matching syntax. I think this extension is actually quite benign, but I put it so low on this list because it seems infrequently used, and I get the sense most people consider it fairly advanced. I would argue, however, that it’s a very pleasant, useful extension, and it’s no more complicated than a number of the features in Haskell 98.</p><h3><a name="intermediate-extensions-to-the-haskell-type-system"></a>Intermediate extensions to the Haskell type system</h3><p>Now we’re getting into the meat of things. Everything up to this point has been, in my opinion, completely self-evident in its usefulness and simplicity. As far as I’m concerned, the extensions in the previous six sections have no business ever being left disabled. Starting in this section, however, I could imagine a valid argument being made either way.</p><p>The following three extensions add some complexity to the Haskell type system in return for some added expressive power:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> are related, given that the former is subsumed by the latter, but <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> also enables an alternative syntax. Both syntaxes allow packing away a typeclass dictionary or equality constraint that is brought into scope upon a successful pattern-match against a data constructor, something that is sometimes quite useful but certainly a departure from Haskell’s simple ADTs.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a> extend multi-parameter typeclasses, and they are almost unavoidable, given their use in the venerable <code>mtl</code> library. Like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a>, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a> add an additional layer of complexity to the typeclass system in order to express certain things that would otherwise be difficult or impossible.</p><p>All of these extensions involve a tradeoff. Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> also implies <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds"><code>MonoLocalBinds</code></a>, which disables let generalization, one of the most likely ways a program that used to typecheck might subsequently fail to do so. Some might argue that this is a good reason to turn <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> on in a per-module basis, but I disagree: I actually want my language to be fairly consistent, and given that I know I am likely going to want to use <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> <em>somewhere</em>, I want <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds"><code>MonoLocalBinds</code></a> enabled <em>everywhere</em>, not inconsistently and sporadically.</p><p>That aside, all these extensions are relatively safe. They are well-understood, and they are fairly self-contained extensions to the Haskell type system. I think these extensions have a very good power to cost ratio, and I find myself using them regularly (especially <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a>), so I keep them enabled globally.</p><h3><a name="advanced-extensions-to-the-haskell-type-system"></a>Advanced extensions to the Haskell type system</h3><p>Finally, we arrive at the last set of extensions in this list. These are the most advanced features Haskell’s type system currently has to offer, and they are likely to be the most controversial to enable globally:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a></p></li></ul><p>All of these extensions exist exclusively for the purpose of type-level programming. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a> allows datatype promotion, creating types that are always uninhabited and therefore can only be used phantom. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> allows the definition of type-level functions that map types to other types. Both of these are minor extensions to Haskell’s surface area, but they have rather significant ramifications on the sort of programming that can be done and the way GHC’s typechecker must operate.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> is an interesting extension because it comes in so many flavors: associated type synonyms, associated datatypes, open and closed type synonym families, and open and closed datatype families. Associated types tend to be easier to grok and easier to use, though they can also be replaced by functional dependencies. Open type families are also quite similar to classes and instances, so they aren’t <em>too</em> tricky to understand. Closed type families, on the other hand, are a rather different beast, and they can be used to do fairly advanced things, <em>especially</em> in combination with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a>.</p><p>I happen to appreciate GHC’s support for these features, and while I’m hopeful that an eventual <code>DependentHaskell</code> will alleviate many of the existing infelicities with dependently typed programming in GHC, in the meantime, it’s often useful to enjoy what exists where practically applicable. Therefore, I have little problem keeping them enabled, since, like the vast majority of extensions on this list, these extensions merely lift restrictions, not adjust semantics of the language without the extensions enabled. When I am going to write a type family, I am going to turn on <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a>; I see no reason to annotate the modules in which I decide to do so. I do not write an annotation at the top of each module in which I define a typeclass or a datatype, so why should I do so with type families?</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a> is a little bit different, since it’s a very new extension, and it doesn’t seem to always work as well as I would hope. Still, when it doesn’t work, it fails with a very straightforward error message, and when it works, it is legitimately useful, so I don’t see any real reason to leave it off if <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> is enabled.</p><h3><a name="extensions-intentionally-left-off-this-list"></a>Extensions intentionally left off this list</h3><p>Given what I’ve said so far, it may seem like I would advocate flipping on absolutely every lever GHC has to offer, but that isn’t actually true. There are a few extensions I quite intentionally do <em>not</em> enable.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XUndecidableInstances"><code>UndecidableInstances</code></a> is something I turn on semi-frequently, since GHC’s termination heuristic is not terribly advanced, but I turn it on per-module, since it’s useful to know when it’s necessary (and in application code, it rarely is). <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverlappingInstances"><code>OverlappingInstances</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XIncoherentInstances"><code>IncoherentInstances</code></a>, in contrast, are completely banned—not only are they almost always a bad idea, GHC has a better, more fine-grained way to opt into overlapping instances, using the <code>{-# OVERLAPPING #-}</code>, <code>{-# OVERLAPPABLE #-}</code>, and <code>{-# INCOHERENT #-}</code> pragmas.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XQuasiQuotes"><code>QuasiQuotes</code></a> are tricky ones. Anecdotes seem to suggest that enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> everywhere leads to worse compile times, but after trying this on a few projects and measuring, I wasn’t able to detect any meaningful difference. Unless I manage to come up with some evidence that these extensions actually slow down compile times just by being <em>enabled</em>, even if they aren’t used, then I may add them to my list of globally-enabled extensions, since I use them regularly.</p><p>Other extensions I haven’t mentioned are probably things I just don’t use very often and therefore haven’t felt the need to include on this list. It certainly isn’t exhaustive, and I add to it all the time, so I expect I will continue to do so in the future. This is just what I have for now, and if your favorite extension isn’t included, it probably isn’t a negative judgement against that extension. I just didn’t think to mention it.</p><h2><a name="libraries-a-field-guide"></a>Libraries: a field guide</h2><p>Now that you’re able to build a Haskell project and have chosen which handpicked flavor of Haskell you are going to write, it’s time to decide which libraries to use. Haskell is an expressive programming language, and the degree to which different libraries can shape the way you structure your code is significant. Picking the right libraries can lead to clean code that’s easy to understand and maintain, but picking the wrong ones can lead to disaster.</p><p>Of course, there are <em>thousands</em> of Haskell libraries on Hackage alone, so I cannot hope to cover all of the ones I have ever found useful, and I certainly cannot cover ones that would be useful but I did not have the opportunity to try (of which there are certainly many). This blog post is long enough already, so I’ll just cover a few categories of libraries that I think I can offer interesting commentary on; most libraries can generally speak for themselves.</p><h3><a name="having-an-effect"></a>Having an effect</h3><p>One of the first questions Haskell programmers bump into when they begin working on a large application is how they’re going to model effects. Few practical programming languages are pure, but Haskell is one of them, so there’s no getting away from coming up with a way to manage side-effects.</p><p>For some applications, Haskell’s built-in solution might be enough: <code>IO</code>. This can work decently for data processing programs that do very minimal amounts of I/O, and the types of side-effects they perform are minimal. For these applications, most of the logic is likely to be pure, which means it’s already easy to reason about and easy to test. For other things, like web applications, it’s more likely that a majority of the program logic is going to be side-effectful by its nature—it may involve making HTTP requests to other services, interacting with a database, and writing to logfiles.</p><p>Figuring out how to structure these effects in a type-safe, decoupled, composable way can be tricky, especially since Haskell has so many different solutions. I could not bring myself to choose just one, but I did choose two: the so-called “<code>mtl</code> style” and freer monads.</p><p><code>mtl</code> style is so named because it is inspired by the technique of interlocking monadic typeclasses and lifting instances used to model effects using constraints that is used in the <a href="https://hackage.haskell.org/package/mtl"><code>mtl</code></a> library. Here is a small code example of what <code>mtl</code> style typeclasses and handlers look like:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MaybeT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="p">(</span><span class="s">"readFile: no such file "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="p">)</span> + +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">delete</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="n">vfs</span></code></pre><p>This is the most prevalent way to abstract over effects in Haskell, and it’s been around for a long time. Due to the way it uses the typeclass system, it’s also very fast, since GHC can often specialize and inline the typeclass dictionaries to avoid runtime dictionary passing. The main drawbacks are the amount of boilerplate required and the conceptual difficulty of understanding exactly how monad transformers, monadic typeclasses, and lifting instances all work together to discharge <code>mtl</code> style constraints.</p><p>There are various alternatives to <code>mtl</code>’s direct approach to effect composition, most of which are built around the idea of reifying a computation as a data structure and subsequently interpreting it. The most popular of these is the <code>Free</code> monad, a clever technique for deriving a monad from a functor that happens to be useful for modeling programs. Personally, I think <code>Free</code> is overhyped. It’s a cute, mathematically elegant technique, but it involves a lot of boilerplate, and composing effect algebras is still a laborious process. The additional expressive power of <code>Free</code>, namely its ability to choose an interpreter dynamically, at runtime, is rarely necessary or useful, and it adds complexity and reduces performance for few benefits. (And in fact, this is still possible to do with <code>mtl</code> style, it’s just uncommon because there is rarely any need to do so.)</p><p>A 2017 blog post entitled <a href="https://markkarpov.com/post/free-monad-considered-harmful.html">Free monad considered harmful</a> discussed <code>Free</code> in comparison with <code>mtl</code> style, and unsurprisingly cast <code>Free</code> in a rather unflattering light. I largely agree with everything outlined in that blog post, so I will not retread its arguments here. I do, however, think that there is another abstraction that <em>is</em> quite useful: the so-called “freer monad” used to implement extensible effects.</p><p>Freer moves even further away from worrying about functors and monads, since its effect algebras do not even need to be functors. Instead, freer’s effect algebras are ordinary GADTs, and reusable, composable effect handlers are easily written to consume elements of these datatypes. Unfortunately, the way this works means that GHC is still not clever enough to optimize freer monads as efficiently as <code>mtl</code> style, since it can’t easily detect when the interpreter is chosen statically and use that information to specialize and inline effect implementations, but the cost difference is significantly reduced, and I’ve found that in real application code, the vast majority of the cost does not come from the extra overhead introduced by a more expensive <code>(&gt;&gt;=)</code>.</p><p>There are a few different implementations of freer monads, but I, sadly, was not satisfied with any of them, so I decided to contribute to the problem by creating yet another one. My implementation is called <a href="https://hackage.haskell.org/package/freer-simple"><code>freer-simple</code></a>, and it includes a streamlined API with <a href="https://hackage.haskell.org/package/freer-simple-1.0.1.1/docs/Control-Monad-Freer.html">more documentation than any other freer implementation</a>. Writing the above <code>mtl</code> style example using <code>freer-simple</code> is more straightforward:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="nb">()</span> + +<span class="nf">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Member</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">send</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span> + +<span class="nf">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Member</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">send</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="nf">runFileSystemIO</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LastMember</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span> +<span class="nf">runFileSystemIO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">interpretM</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="kr">case</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="nf">runFileSystemInMemory</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">effs</span> +<span class="nf">runFileSystemInMemory</span><span class="w"> </span><span class="n">initVfs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runState</span><span class="w"> </span><span class="n">initVfs</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">State</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span> +<span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">reinterpret</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">case</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="p">(</span><span class="s">"readFile: no such file "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="p">)</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">delete</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="n">vfs</span></code></pre><p>(It could be simplified further with a little bit of Template Haskell to generate the <code>readFile</code> and <code>writeFile</code> function definitions, but I haven’t gotten around to writing that.)</p><p>So which effect system do I recommend? I used to recommend <code>mtl</code> style, but as of only two months ago, I now recommend <code>freer-simple</code>. It’s easier to understand, involves less boilerplate, achieves “good enough” performance, and generally gets out of the way wherever possible. Its API is designed to make it easy to do the sorts of the things you most commonly need to do, and it provides a core set of effects that can be used to build a real-world application.</p><p>That said, freer is indisputably relatively new and relatively untested. It has success stories, but <code>mtl</code> style is still the approach used by the majority of the ecosystem. <code>mtl</code> style has more library support, its performance characteristics are better understood, and it is a tried and true way to structure effects in a Haskell application. If you understand it well enough to use it, and you are happy with it in your application, my recommendation is to stick with it. If you find it confusing, however, or you end up running up against its limits, give <code>freer-simple</code> a try.</p><h3><a name="through-the-looking-glass-to-lens-or-not-to-lens"></a>Through the looking glass: to lens or not to lens</h3><p>There’s no getting around it: <a href="https://hackage.haskell.org/package/lens"><code>lens</code></a> is a behemoth of a library. For a long time, I wrote Haskell without it, and honestly, it worked out alright. I just wasn’t doing a whole lot of work that involved complicated, deeply-nested data structures, and I didn’t feel the need to bring in a library with such a reputation for having impenetrable operators and an almost equally impenetrable learning curve.</p><p>But, after some time, I decided I wanted to take the plunge. So I braced myself for the worst, pulled out my notebook, and started writing some code. To my surprise… it wasn’t that hard. It made sense. Sure, I still don’t know how it works on the inside, and I never did learn the majority of the exports in <code>Control.Lens.Operators</code>, but I had no need to. Lenses were useful in the way I had expected them to be, and so were prisms. One thing led to another, and before long, I understood the relationship between the various optics, the most notable additions to my toolkit being folds and traversals. Sure, the type errors were completely opaque much of the time, but I was able to piece things together with ample type annotations and time spent staring at ill-typed expressions. Before long, I had developed an intuition for <code>lens</code>.</p><p>After using it for a while, I retrospected on whether or not I liked it, and honestly, I still can’t decide. Some lensy expressions were straightforward to read and were a pleasant simplification, like this one:</p><pre><code class="pygments"><span class="nf">paramSpecs</span><span class="w"> </span><span class="o">^..</span><span class="w"> </span><span class="n">folded</span><span class="o">.</span><span class="n">_Required</span></code></pre><p>Others were less obviously improvements, such as this beauty:</p><pre><code class="pygments"><span class="kt">M</span><span class="o">.</span><span class="n">fromList</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">paramSpecs</span><span class="w"> </span><span class="o">^..</span><span class="w"> </span><span class="n">folded</span><span class="o">.</span><span class="n">_Optional</span><span class="o">.</span><span class="n">filtered</span><span class="w"> </span><span class="p">(</span><span class="n">has</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">_2</span><span class="o">.</span><span class="n">_UsePreviousValue</span><span class="p">)</span></code></pre><p>But operator soup aside, there was something deeper about <code>lens</code> that bothered me, and I just wasn’t sure what. I didn’t know how to articulate my vague feelings until I read a 2014 blog post entitled <a href="https://ro-che.info/articles/2014-04-24-lens-unidiomatic">Lens is unidiomatic Haskell</a>, which includes a point that I think is spot-on:</p><blockquote><p>Usually, types in Haskell are rigid. This leads to a distinctive style of composing programs: look at the types and see what fits where. This is impossible with <code>lens</code>, which takes overloading to the level mainstream Haskell probably hasn’t seen before.</p><p>We have to learn the new language of the <code>lens</code> combinators and how to compose them, instead of enjoying our knowledge of how to compose Haskell functions. Formally, <code>lens</code> types are Haskell function types, but while with ordinary Haskell functions you immediately see from types whether they can be composed, with <code>lens</code> functions this is very hard in practice.</p><p>[…]</p><p>Now let me clarify that this doesn’t necessarily mean that <code>lens</code> is a bad library. It’s an <em>unusual</em> library. It’s almost a separate language, with its own idioms, embedded in Haskell.</p></blockquote><p>The way <code>lens</code> structures its types deliberately introduces a sort of subtyping relationship—for example, all lenses are traversals and all traversals are folds, but not vice versa—and indeed, knowing this subtyping relationship is essential to working with the library and understanding how to use it. It is helpfully documented with a large diagram on <a href="https://hackage.haskell.org/package/lens">the <code>lens</code> package overview page</a>, and that diagram was most definitely an invaluable resource for me when I was learning how to use the library.</p><p>On the surface, this isn’t unreasonable. Subtyping is an enormously useful concept! The only reason Haskell dispenses with it entirely is because it makes type inference notoriously difficult. The subtyping relation between optics is one of the things that makes them so useful, since it allows you to easily compose a lens with a prism and get a traversal out. Unfortunately, the downside of all this is that Haskell does not truly have subtyping, so all of <code>lens</code>’s “types” really must be type aliases for types of roughly the same shape, namely functions. This makes type errors completely <em>baffling</em>, since the errors do not mention the aliases, only the fully-expanded types (which are often rather complicated, and their meaning is not especially clear without knowing how <code>lens</code> works under the hood).</p><p>So the above quote is correct: working with <code>lens</code> really <em>is</em> like working in a separate embedded language, but I’m usually okay with that. Embedded, domain-specific languages are good! Unfortunately, in this case, the host language is not very courteous to its guest. Haskell does not appear to be a powerful enough language for <code>lens</code> to be a language in its own right, so it must piggyback on top of Haskell’s error reporting mechanisms, which are insufficient for <code>lens</code> to be a cohesive linguistic abstraction. Just as debugging code by stepping through the assembly it produces (or, perhaps more relevant in 2018, debugging a compile-to-JS language by looking at the emitted JavaScript instead of the source code) makes for an unacceptably leaky language. We would never stand for such a thing in our general-purpose language tooling, and we should demand better even in our embedded languages.</p><p>That said, <code>lens</code> is just too useful to ignore. It is a hopelessly leaky abstraction, but it’s still an abstraction, and a powerful one at that. Given my selection of default extensions as evidence, I think it’s clear I have zero qualms with “advanced” Haskell; I will happily use even <code>singletons</code> where it makes sense. Haskell’s various language extensions are sometimes confusing in their own right, but their complexity is usually fundamental to the expressive power they bring. <code>lens</code> has some fundamental complexity, too, but it is mostly difficult for the wrong reasons. Still, while it is not the first library I reach for on every new Haskell project, manipulating nested data without <code>lens</code> is just too unpleasant after tasting the nectar, so I can’t advise against it in good faith.</p><p>Sadly, this means I’m a bit wishy-washy when it comes to using <code>lens</code>, but I do have at least one recommendation: if you decide to use <code>lens</code>, it’s better to go all-in. Don’t generate lenses for just a handful of datatypes, do it for <em>all</em> of them. You can definitely stick to a subset of the <code>lens</code> library’s features, but don’t apply it in some functions but not others. Having too many different, equally valid ways of doing things leads to confusion and inconsistency, and inconsistency minimizes code reuse and leads to duplication and spaghetti. Commit to using <code>lens</code>, or don’t use it at all.</p><h3><a name="mitigating-the-string-problem"></a>Mitigating the string problem</h3><p>Finally, Haskell has a problem with strings. Namely, <code>String</code> is a type alias for <code>[Char]</code>, a lazy, singly linked list of characters, which is an awful representation of text. Fortunately, the answer to this problem is simple: ban <code>String</code> in your programs.</p><p>Use <code>Text</code> everywhere. I don’t really care if you pick strict <code>Text</code> or lazy <code>Text</code>, but pick one and stick to it. Don’t ever use <code>String</code>, and <em>especially</em> don’t ever, <em>ever</em>, <em><strong>ever</strong></em> use <code>ByteString</code> to represent text! There are enormously few legitimate cases for using <code>ByteString</code> in a program that is not explicitly about reading or writing raw data, and even at that level, <code>ByteString</code> should only be used at program boundaries. In that sense, I treat <code>ByteString</code> much the same way I treat <code>IO</code>: push it to the boundaries of your program.</p><p>One of Haskell’s core tenets is making illegal states unrepresentable. Strings are not especially useful datatypes for this, since they are sequences of arbitrary length made up of atoms that can be an enormously large number of different things. Still, string types enforce a very useful invariant, a notion of a sequence of human-readable characters. In the presence of Unicode, this is a more valuable abstraction than it might seem, and the days of treating strings as little different from sequences of bytes are over. While strings make a poor replacement for enums, they are quite effective at representing the incredible amount of text humans produce in a staggeringly large number of languages, and they are the right type for that job.</p><p><code>ByteString</code>, on the other hand, is essentially never the right type for any job. If a type classifies a set of values, <code>ByteString</code> is no different from <code>Any</code>. It is the structureless type, the all-encompassing blob of bits. A <code>ByteString</code> could hold anything at all—some text, an image, an executable program—and the type system certainly isn’t going to help to answer that question. The only use case I can possibly imagine for passing around a <code>ByteString</code> in your program rather than decoding it into a more precise type is if it truly holds opaque data, e.g. some sort of token or key provided by a third party with no structure guaranteed whatsoever. Still, even this should be wrapped in a <code>newtype</code> so that the type system enforces this opaqueness.</p><p>Troublingly, <code>ByteString</code> shows up in many libraries’ APIs where it has no business being. In many cases, this seems to be things where ASCII text is expected, but this is hardly a good reason to willingly accept absolutely anything and everything! Make an <code>ASCII</code> type that forbids non-ASCII characters, and provide a <code>ByteString -&gt; Maybe ASCII</code> function. Alternatively, think harder about your problem in question to properly support Unicode as you almost certainly ought to.</p><p>Other places <code>ByteString</code> appears are similarly unfortunate. Base-64 encoding, for example, could be given the wonderfully illustrative type <code>ByteString -&gt; Text</code>, or even <code>ByteString -&gt; ASCII</code>! Such a type makes it immediately clear why base-64 is useful: it allows transforming arbitrary binary data into a reliable textual encoding. If we consider that <code>ByteString</code> is essentially <code>Any</code>, this function has the type <code>Any -&gt; ASCII</code>, which is amazingly powerful! We can convert <em>anything</em> to ASCII text!</p><p>Existing libraries, however, just provide the boring, disappointingly inaccurate type <code>ByteString -&gt; ByteString</code>, which is one of the most useless types there is. It is essentially <code>Any -&gt; Any</code>, the meaningless function type. It conveys nothing about what it does, other than that it is pure. Giving a function this type is scarcely better than dynamic typing. Its mere existence is a failure of Haskell library design.</p><p>But wait, it gets worse! <code>Data.Text.Encoding</code> exports a function called <code>decodeUtf8</code>, which has type <code>ByteString -&gt; Text</code>. What an incredible function with a captivating type! Whatever could it possibly do? Again, this function’s type is basically <code>Any -&gt; Text</code>, which is remarkable in the power it gives us. Let’s try it out, shall we?</p><pre><code>ghci&gt; decodeUtf8 "\xc3\x28" +"*** Exception: Cannot decode byte '\x28': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream +</code></pre><p>Oh. Well, that’s a disappointment.</p><p>Haskell’s string problem goes deeper than <code>String</code> versus <code>Text</code>; it seems to have wound its way around the collective consciousness of the Haskell community and made it temporarily forget that it cares about types and totality. This isn’t that hard, I swear! I can only express complete befuddlement at how many of these APIs are just completely worthless.</p><p>Fortunately, there is a way out, and that way out is <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a>. It is the first Haskell library I ever wrote. It provides <em>type safe</em>, <em>total</em> conversions between <code>Text</code> and various other types, and it is encoding aware. It provides appropriately-typed base-16 and base-64 conversion functions, and is guaranteed to never raise any exceptions. Use it, and apply the Haskell philosophy to your strings, just as you already do for everything else in your program.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p><em>Phew.</em></p><p>When I started writing this blog post, it used the phrase “short overview” in the introduction. It is now over ten thousand words long. I think that’s all I have it in me to say for now.</p><p>Haskell is a wonderful language built by a remarkable group of people. Its community is often fraught with needlessly inflammatory debates about things like the value of codes of conduct, the evils of Hackage revisions, and precisely how much or how little people ought to care about the monad laws. These flame wars frustrate me to no end, and they sometimes go so far as to make me ashamed to call myself a part of the Haskell community. Many on the “outside” seem to view Haskellers as an elitist, mean-spirited cult, more interested in creating problems for itself than solving them.</p><p>That perception is categorically wrong.</p><p>I have never been in a community of programmers so dedicated and passionate about applying thought and rigor to building software, then going out and <em>actually doing it</em>. I don’t know anywhere else where a cutting-edge paper on effect systems is discussed by the very same people who are figuring out how to reliably deploy distributed services to AWS. Some people view the Haskell community as masturbatory, and to some extent, they are probably right. One of my primary motivators for writing Haskell is that it is fun and it challenges me intellectually in ways that other languages don’t. But that challenge is not a sign of uselessness, it is a sign that Haskell is <em>so close</em> to letting me do the right thing, to solving the problem the right way, to letting me work without compromises. When I write in most programming languages, I must constantly accept that my program will never be robust in all the ways I want it to be, and I might as well give up before I even start. Haskell’s greatest weakness is that it tempts me to try.</p><p>Haskell is imperfect, as it will always be. I doubt I will ever be satisfied by any language or any ecosystem. There will always be more to learn, more to discover, better tools and abstractions to develop. Many of them will not look anything like Haskell; they may not involve formal verification or static types or effect systems at all. Perhaps live programming, structural editors, and runtime hotswapping will finally take over the world, and we will find that the problems we thought we were solving were irrelevant to begin with. I can’t predict the future, and while I’ve found great value in the Haskell school of program construction, I dearly hope that we do not develop such tunnel vision that we cannot see that there may be other ways to solve these problems. Many of the solutions are things we likely have not even begun to think about. Still, whether that happens or not, it is clear to me that Haskell is a point in the design space unlike any other, and we learn almost as much from the things it gets wrong as we do from the things it gets right.</p><p>It’s been a wonderful two years, Haskell. I won’t be a stranger.</p><ol class="footnotes"></ol></article>Hackett progress report: documentation, quality of life, and snake2017-08-28T00:00:00Z2017-08-28T00:00:00ZAlexis King<article><p>Three months ago, <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">I wrote a blog post describing my new, prototype implementation of my programming language, Hackett</a>. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/"><strong>the first (albeit quite incomplete) approach to Hackett’s documentation</strong></a>.</p><p>I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.</p><h2><a name="a-philosophy-of-documentation"></a>A philosophy of documentation</h2><p>Racket, as a project, has always had <a href="http://docs.racket-lang.org">wonderful documentation</a>. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably <a href="http://docs.racket-lang.org/scribble/index.html">Scribble, the Racket documentation tool</a>. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with <a href="http://docs.racket-lang.org/scribble/plt-manuals.html">a powerful library for documenting Racket code</a>. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.</p><p>Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an <code>@</code> character instead of <code>\</code>. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use <a href="http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29">the provided <code>defproc</code> form</a>:</p><pre><code class="pygments"><span class="n">@defproc</span><span class="p">[(</span><span class="nb">add1</span><span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="nb">number?</span><span class="p">])</span><span class="w"> </span><span class="nb">number?</span><span class="p">]{</span> +<span class="n">Returns</span><span class="w"> </span><span class="n">@racket</span><span class="p">[(</span><span class="nb">+</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="mi">1</span><span class="p">)]</span><span class="o">.</span><span class="p">}</span></code></pre><p>This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into <a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29">something like this</a>:</p><p><a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29"></a></p><p>The fact that Scribble documents are fully-fledged <em>programs</em> equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is <a href="http://docs.racket-lang.org/scribble/eval.html">the <code>scribble/example</code> module</a>, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special <code>eval:check</code> form that enables <a href="https://docs.python.org/3/library/doctest.html">doctest</a>-like checking, which allows documentation to serve double duty as a test suite.</p><p>Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is <em>designed</em> with the goal of supporting many different programming languages, and Scribble is no exception. Things like <a href="http://docs.racket-lang.org/scribble/eval.html"><code>scribble/example</code></a> essentially work out of the box with Hackett, and most of <a href="http://docs.racket-lang.org/scribble/plt-manuals.html"><code>scribble/manual</code></a> can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The <code>defproc</code> and <code>defstruct</code> forms are hardly builtins; they are defined as part of the <code>scribble/manual</code> library in terms of Scribble primitives, and <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt">we can do the same</a>.</p><p>Hackett’s documentation already defines three new forms, <code>defdata</code>, <code>defclass</code>, and <code>defmethod</code>, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the <code>Functor</code> typeclass using <code>defclass</code> like this:</p><pre><code class="pygments"><span class="n">@defclass</span><span class="p">[(</span><span class="n">Functor</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="nb">map</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="p">)})]]{</span> + +<span class="n">A</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">@deftech</span><span class="p">{</span><span class="n">functors</span><span class="p">}</span><span class="o">,</span><span class="w"> </span><span class="n">essentially</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="k">provide</span><span class="w"> </span><span class="n">a</span> +<span class="n">mapping</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">“piercing”</span><span class="w"> </span><span class="n">operation.</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">@racket</span><span class="p">[</span><span class="nb">map</span><span class="p">]</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">viewed</span><span class="w"> </span><span class="n">in</span> +<span class="n">different</span><span class="w"> </span><span class="n">ways:</span> + +<span class="k">...</span><span class="p">}</span></code></pre><p>With only a little more than the above code, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29">Hackett’s documentation includes a beautifully-typeset definition of the <code>Functor</code> typeclass</a>, including examples and rich prose:</p><p><a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29"></a></p><p>Scribble makes Hackett’s documentation shine.</p><h3><a name="a-tale-of-two-users"></a>A tale of two users</h3><p>For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.</p><p>A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.</p><p>However, for experienced users, an authoritative reference is <em>exactly</em> what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.</p><p>This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:</p><ul><li><p>Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.</p><p><a href="https://docs.oracle.com/javase/8/docs/api/">Java’s API documentation</a> documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly <em>small</em> language that does not often change.</p><p>On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java <em>does</em> provide guide-style documentation in the form of the <a href="https://docs.oracle.com/javase/tutorial/">Java Tutorials</a>, but they are of inconsistent quality.</p><p>More importantly, while the Java tutorials link to the API docs, the reverse is <strong>not</strong> true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.</p><p>Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.</p></li><li><p>Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.</p><p>The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is <a href="https://developer.mozilla.org/en-US/">MDN</a>. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.</p><p>The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.</p><p>This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. <a href="http://ramdajs.com/docs/">Ramda’s documentation is nothing but a reference</a>, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, <a href="http://passportjs.org/docs">Passport’s docs are essentially <em>only</em> a set of tutorials</a>, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method <em>does</em>. Fortunately, <a href="https://facebook.github.io/react/docs/hello-world.html">there are some libraries, like React</a>, that absolutely <em>nail</em> this, and they have both styles of documentation that are <strong>actually cross-referenced</strong>. Unfortunately, those are mostly the exceptions, not the norm.</p></li><li><p><a href="https://docs.python.org/3/index.html">Python’s documentation is interesting</a>, since it includes a set of tutorials alongside the API reference, and it <em>also</em> ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on <code>if</code> links to the section in the reference about <code>if</code>, but nothing goes in the other direction, which is something of a missed opportunity.</p></li><li><p><a href="https://hackage.haskell.org/package/base">Haskell manages to be especially bad here</a> (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.</p><p>It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and <a href="https://www.haskell.org/haddock/">Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look</a>.</p></li></ul><p>The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with <em>two</em> pieces of documentation: the <a href="http://docs.racket-lang.org/guide/index.html">Racket Guide</a> and the <a href="http://docs.racket-lang.org/reference/index.html">Racket Reference</a>. The guide includes over <strong>one hundred thousand</strong> words of explanations and examples, and the reference includes roughly <strong>half a million</strong>. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.</p><p>Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the <a href="http://docs.racket-lang.org/hackett/guide.html">Hackett Guide</a> and the <a href="http://docs.racket-lang.org/hackett/reference.html">Hackett Reference</a>, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily <em>need</em> to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a <em>lot</em> more accessible without making it any less useful for power users.</p><h2><a name="rounding-hackett-s-library-sanding-its-edges"></a>Rounding Hackett’s library, sanding its edges</h2><p>One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a <em>lot</em> of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.</p><p>Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:</p><ul><li><p>Hackett includes a <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29"><code>Double</code></a> type for working with IEEE 754 double-precision floating-point numbers.</p></li><li><p>Local definitions are supported via the <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29"><code>let</code></a> and <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29"><code>letrec</code></a> forms.</p></li><li><p>The prelude includes many more functions, especially <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29">functions on lists</a>.</p></li><li><p>The Hackett reader has been adjusted to support using <code>.</code> as a bare symbol, since <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29"><code>.</code> is the function composition operator</a>.</p></li><li><p>The Hackett REPL supports many more forms, including <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29">ADT</a>, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29">class</a>, and <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29">instance</a> definitions. Additionally, the REPL now uses <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a> instances to display the results of expressions. To compensate for the inability to print non-<a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a>able things, a new <code>(#:type expr)</code> syntax is permitted to print the type of <em>any</em> expression.</p></li><li><p>Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.</p></li></ul><p>Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189">heavily commented</a> with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.</p><p>One of the most exciting things about documenting Hackett has been realizing just <em>how much</em> already exists. Seriously, if you have gotten to this point in the blog post but haven’t read <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/">the actual documentation</a> yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for <em>me</em>: it’s a language I’d like to use.</p><p>Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!</p><h2><a name="a-demo-with-pictures"></a>A demo with pictures</h2><p>Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?</p><p>Hackett ships with a special package of demo libraries in the aptly-named <code>hackett-demo</code> package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">the previous Hackett blog post</a>, I demonstrated the capabilities of <code>hackett/demo/web-server</code>. In this blog post, we’re going to use <code>hackett/demo/pict</code> and <code>hackett/demo/pict/universe</code>, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!</p><p>As always, we’ll start with <code>#lang hackett</code>, and we’ll import the necessary libraries:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/pict</span> +<span class="w"> </span><span class="n">hackett/demo/pict/universe</span><span class="p">)</span></code></pre><p>With that, we can start immediately with a tiny example. Just to see how <code>hackett/demo/pict</code> works, let’s start by rendering a red square. We can do this by writing a <code>main</code> action that calls <code>print-pict</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))))</span></code></pre><p>If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!</p><p></p><p>Using the REPL, we can inspect the type of <code>print-pict</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">print-pict</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Unit</span><span class="p">))</span></code></pre><p>Unsurprisingly, displaying a picture to the screen needs <code>IO</code>. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of <code>filled-square</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">filled-square</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">Pict</span><span class="p">)</span></code></pre><p>No <code>IO</code> to be seen! This is because “picts” are entirely <em>pure</em> values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">{(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))</span> +<span class="w"> </span><span class="n">hc-append</span> +<span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))}))</span></code></pre><p>This code will print out a red square to the left of a blue one.</p><p></p><p>Again, <code>hc-append</code> is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">hc-append</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="n">Pict</span><span class="p">))</span></code></pre><p>Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!</p><h3><a name="implementing-a-snake-clone"></a>Implementing a snake clone</h3><p>This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the <code>pict</code> or <code>universe</code> libraries. Hopefully it’s still illustrative.</p><p>We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an <code>Eq</code> instance for our points.</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="n">d:left</span><span class="w"> </span><span class="n">d:right</span><span class="w"> </span><span class="n">d:up</span><span class="w"> </span><span class="n">d:down</span><span class="p">)</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">Eq</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="k">==</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)]</span><span class="w"> </span><span class="p">{{</span><span class="n">a</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">c</span><span class="p">}</span><span class="w"> </span><span class="n">&amp;&amp;</span><span class="w"> </span><span class="p">{</span><span class="n">b</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">d</span><span class="p">}})])</span></code></pre><p>With these two datatypes, we can implement a <code>move</code> function that accepts a point and a direction and produces a new point for an adjacent tile:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Direction</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:left</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:right</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:up</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">})]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:down</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">})])</span></code></pre><p>The next step is to define a type for our world state. The <code>big-bang</code> library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span> +<span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="c1">; snake direction</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; snake blocks</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; food blocks</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">set-ws-direction</span><span class="w"> </span><span class="p">[[</span><span class="n">d</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)])</span></code></pre><p>Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-width</span><span class="w"> </span><span class="mi">50</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-height</span><span class="w"> </span><span class="mi">30</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="p">{(</span><span class="n">d*</span><span class="w"> </span><span class="mf">15.0</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">integer-&gt;double</span><span class="p">})</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="p">(</span><span class="n">blank-rect</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-height</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">13.0</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="n">block</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="n">block</span><span class="p">))</span></code></pre><p>Now we can write our actual <code>render</code> function. To do this, we simply need to render each <code>Point</code> in our <code>World-State</code>’s two lists as a block on an <code>empty-board</code>. We’ll write a helper function, <code>render-on-board</code>, which does exactly that:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render-on-board</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Pict</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">pict</span><span class="w"> </span><span class="n">points</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">foldr</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">acc</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">pict</span><span class="p">))</span> +<span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="n">points</span><span class="p">)])</span></code></pre><p>This function uses <code>foldr</code> to collect each point and place the provided pict at the right location using <code>pin-over</code> on an empty board. Using <code>render-on-board</code>, we can write the <code>render</code> function in just a couple of lines:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)</span> +<span class="w"> </span><span class="mf">0.0</span><span class="w"> </span><span class="mf">0.0</span> +<span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="n">food-points</span><span class="p">))])</span></code></pre><p>Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the <code>big-bang</code> library provides a <code>random-integer</code> function, which we can use to write a <code>random-point</code> action:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">random-point</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">point</span><span class="w"> </span><span class="n">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span> +<span class="w"> </span><span class="n">&lt;*&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-height</span><span class="p">)})</span></code></pre><p>Hackett supports applicative notation using infix operators, so <code>random-point</code> looks remarkably readable. It also runs in <code>IO</code>, since the result is, obviously, random. Fortunately, the <code>on-tick</code> function runs in <code>IO</code> as well (unlike <code>render</code>, which must be completely pure), so we can use <code>random-point</code> when necessary to generate a new food block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">init!</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)})</span> +<span class="w"> </span><span class="p">{</span><span class="nb">reverse</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tail!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="nb">reverse</span><span class="p">})</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">new-snake-point</span><span class="w"> </span><span class="p">(</span><span class="n">move</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">(</span><span class="n">head!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">elem?</span><span class="w"> </span><span class="n">food-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">random-point</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">snake-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">delete</span><span class="w"> </span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">food-points</span><span class="p">)})))</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">init!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)}</span> +<span class="w"> </span><span class="n">food-points</span><span class="p">))))])</span></code></pre><p>This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to <code>new-snake-point</code>, then checks if there is a food block at that location. If there is, it generates a <code>new-food-point</code>, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.</p><p>The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our <code>set-ws-direction</code> function that we wrote earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-key</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">KeyEvent</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:left</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:left</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:right</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:right</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:up</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:up</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:down</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:down</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="k">_</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id</span><span class="p">}])</span></code></pre><p>The <code>on-key</code> function runs in <code>IO</code>, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in <code>pure</code>.</p><p>We’re almost done now—all we need to do is set up the <em>initial</em> state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">initial-state</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">initial-food</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">sequence</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="n">repeat</span><span class="w"> </span><span class="n">random-point</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d:right</span> +<span class="w"> </span><span class="p">{(</span><span class="n">point</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">24</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">23</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">nil</span><span class="p">}</span> +<span class="w"> </span><span class="n">initial-food</span><span class="p">))))</span></code></pre><p>Notably, we can use the <code>repeat</code> function to create an infinite list of <code>random-point</code> actions, <code>take</code> the first five of them, then call <code>sequence</code> to execute them from left to right. Now, all we have to do is put the pieces together in a <code>main</code> block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">state</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">initial-state</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">big-bang</span><span class="w"> </span><span class="n">state</span> +<span class="w"> </span><span class="kd">#:to-draw</span><span class="w"> </span><span class="n">render</span> +<span class="w"> </span><span class="kd">#:on-tick</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="mf">0.2</span> +<span class="w"> </span><span class="kd">#:on-key</span><span class="w"> </span><span class="n">on-key</span><span class="p">)))</span></code></pre><p>And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.</p><p></p><h2><a name="contributing-to-hackett"></a>Contributing to Hackett</h2><p>If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.</p><p>On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, <a href="https://github.com/lexi-lambda/hackett/issues">Hackett has an issue tracker</a>, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.</p><p>Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to <a href="https://github.com/lexi-lambda/hackett/issues">open an issue on GitHub</a>, send me a message on the <code>#racket</code> IRC channel on Freenode, or ping me on <a href="http://racket-slack.herokuapp.com">the Racket Slack team</a>.</p><h2><a name="acknowledgements"></a>Acknowledgements</h2><p>Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to <a href="https://github.com/gelisam">Samuel Gélineau, aka gelisam</a>, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.</p><p>Additionally, I want to specially thank <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.</p><p>Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.</p><ol class="footnotes"></ol></article>Unit testing effectful Haskell with monad-mock2017-06-29T00:00:00Z2017-06-29T00:00:00ZAlexis King<article><p>Nearly eight months ago, <a href="/blog/2016/10/03/using-types-to-unit-test-in-haskell/">I wrote a blog post about unit testing effectful Haskell code</a> using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, <a href="https://hackage.haskell.org/package/monad-mock">monad-mock</a>.</p><h2><a name="a-first-glance-at-monad-mock"></a>A first glance at monad-mock</h2><p>The monad-mock library is, first and foremost, designed to be <em>easy</em>. It doesn’t ask much from you, and it requires almost zero boilerplate.</p><p>The first step is to write an mtl-style interface that encodes an effect you want to mock. For example, you might want to test some code that interacts with the filesystem:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Now you just have to write your code as normal. For demonstration purposes, here’s a function that defines copying a file in terms of <code>readFile</code> and <code>writeFile</code>:</p><pre><code class="pygments"><span class="nf">copyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>Making this function work on the real filesystem is trivial, since we just need to define an instance of <code>MonadFileSystem</code> for <code>IO</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span></code></pre><p>But how do we test this? Well, we <em>could</em> run some real code in <code>IO</code>, which might not be so bad for such a simple function, but this seems like a bad idea. For one thing, a bad implementation of <code>copyFile</code> could do some pretty horrible things if it misbehaved and decided to overwrite important files, and if you’re constantly running a test suite whenever a file changes, it’s easy to imagine causing a lot of damage. Running tests against the real filesystem also makes tests slower and harder to parallelize, and it only gets much worse once you are doing more complex effects than interacting with the filesystem.</p><p>Using monad-mock, we can test this function in just a couple of lines of code:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Exception</span><span class="w"> </span><span class="p">(</span><span class="nf">evaluate</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock.TH</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Data.Function</span><span class="w"> </span><span class="p">((</span><span class="o">&amp;</span><span class="p">))</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Test.Hspec</span> + +<span class="nf">makeMock</span><span class="w"> </span><span class="s">"FileSystemAction"</span><span class="w"> </span><span class="p">[</span><span class="n">ts</span><span class="o">|</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="o">|</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"copyFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reads a file and writes its contents to another file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span></code></pre><p>That’s it!</p><p>The last two lines of the above snippet are the real interesting bits, which specify the actions that are expected to be executed, and it couples them with their results. You will find that if you tweak the list in any way, such as reordering the actions, eliminating one or both of them, or adding an additional action to the end, the test will fail. We could even turn this into a property-based test that generated arbitrary file paths and file contents.</p><p>Admittedly, in this trivial example, the mock is a little silly, since converting this into a property-based test would demonstrate how much we’ve basically just reimplemented the function in our test. However, once our function starts to do somewhat more complicated things, then our tests become more meaningful. Here’s a similar function that only copies a file if it is nonempty:</p><pre><code class="pygments"><span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This function has some logic which is very clearly <em>not</em> expressed in its type, and it would be difficult to encode that information into the type in a safe way. Fortunately, we can guarantee that it works by writing some tests:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">""</span><span class="w"> </span><span class="p">]</span></code></pre><p>These tests are much more useful, and they have some actual value to them. Imagine we had accidentally written <code>when</code> instead of <code>unless</code>, an easy typo to make. Our tests would fail with some useful error messages:</p><pre><code>1) copyNonemptyFile copies a file with contents + uncaught exception: runMockT: expected the following unexecuted actions to be run: + WriteFile "bar.txt" "contents" + +2) copyNonemptyFile does nothing with an empty file + uncaught exception: runMockT: expected end of program, called writeFile + given action: WriteFile "bar.txt" "" +</code></pre><p>You now know enough to write tests with monad-mock.</p><h2><a name="why-unit-test"></a>Why unit test?</h2><p>When the issue of testing is brought up in Haskell, it is often treated with a certain distaste by a portion of the community. There are some points I’ve seen a number of times, and though they take different forms, they boil down to two ideas:</p><ol><li><p>“Haskell code does not need tests because the type system can prove correctness.”</p></li><li><p>“Testing in Haskell is trivial because it is a pure language, and testing pure functions is easy.”</p></li></ol><p>I’ve been writing Haskell professionally for over a year now, and I can happily say that there <em>is</em> some truth to both of those things! When my Haskell code typechecks, I feel a confidence in it that I would not feel were I using a language with a less powerful type system. Furthermore, Haskell encourages a “pure core, impure shell” approach to system design that makes testing many things pleasant and straightforward, and it completely eliminates the worry of subtle nondeterminism leaking into tests.</p><p>That said, Haskell is not a proof assistant, and its type system cannot guarantee everything, especially for code that operates on the boundaries of what Haskell can control. For much the same reason, I find that my pure code is the code I am <em>least</em> likely to need to test, since it is also the code with the strongest type safety guarantees, operating on types in my application’s domain. In contrast, the effectful code is often what I find the most value in extensively testing, since it often contains the most subtle complexity, and it is frequently difficult or even impossible to encode into types.</p><p>Haskell has the power to provide remarkably strong correctness guarantees with a surprisingly small amount of effort by using a combination of tests and types, using each to accommodate for the other’s weaknesses and playing to each technique’s strengths. Some code is test-driven, other code is type-driven. Most code ends up being a mix of both. Testing is just a tool like any other, and it’s nice to feel confident in one’s ability to effectively structure code in a decoupled, testable manner.</p><h2><a name="why-mock"></a>Why mock?</h2><p>Even if you accept that testing is good, the question of whether or not to <em>mock</em> is a subtler issue. To some people, “unit testing” is synonymous with mocks. This is emphatically not true, and in fact, overly aggressive mocking is one of the best ways to make your test suite completely worthless. The monad-mock approach to mocking is a bit more principled than mocking in many dynamic, object-oriented languages, but it comes with many of the same drawbacks: mocks couple your tests to your implementation in ways that make them less valuable and less meaningful.</p><p>For the <code>MonadFileSystem</code> example above, I would actually probably <em>not</em> use a mock. Instead, I would use a <strong>fake</strong>, in-memory filesystem implementation:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)])</span> +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">second</span><span class="w"> </span><span class="n">sort</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">runStateT</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">fs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="o">&amp;</span> +<span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"readFile: no such file ‘"</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"’"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">filter</span><span class="w"> </span><span class="p">((</span><span class="o">/=</span><span class="w"> </span><span class="n">path</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fst</span><span class="p">)</span><span class="w"> </span><span class="n">fs</span></code></pre><p>The above snippet demonstrates how easy it is to define a <code>MonadFileSystem</code> implementation in terms of <code>StateT</code>, and while this may seem like a lot of boilerplate, it really isn’t. You have to write a fake <em>once</em> per interface, and the above block is a minuscule twelve lines of code. With this technique, you are still able to write tests that depend on the state of the filesystem before and after running the implementation, but you decouple yourself from the precise process of getting there:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"bar.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">),</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is better than using a mock, and I would highly recommend doing it if you can! However, a lot of real applications have to interact with services of much greater complexity than an idealized filesystem, and creating that sort of in-memory fake is not always practical. One such situation might be interacting with AWS CloudFormation, for example:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">createStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackName</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StackTemplate</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackId</span><span class="p">)</span> +<span class="w"> </span><span class="n">listStacks</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="p">[</span><span class="kt">StackSummaries</span><span class="p">])</span> +<span class="w"> </span><span class="n">describeStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackId</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackInfo</span><span class="p">)</span> +<span class="w"> </span><span class="c1">-- and so on...</span></code></pre><p>AWS is a very complex system, and it can do dozens of different things (and fail in dozens of different ways) based on an equally complex set of inputs. For example, in the above API, <code>createStack</code> needs to parse its template, which can be YAML or JSON, in order to determine which of many possible errors and behaviors can be produced, both on the initial call and on subsequent ones.</p><p>Creating a fake implementation of <em>AWS</em> is hardly feasible, and this is where a mock can be useful. By simply writing <code>makeMock "AWSAction" [ts| MonadAWS |]</code>, we can test functions that interact with AWS in a pure way without necessarily needing to replicate all of its complexity.</p><h3><a name="isolating-mocks"></a>Isolating mocks</h3><p>Of course, tests that use mocks provide less value than tests that use “smarter” fakes, since they are far more tightly coupled to the implementation, and it’s dramatically more likely that you will need to change the tests when you change the logic. To avoid this, it can be helpful to create multiple interfaces to the same thing: a high-level interface and a low-level one. If our above <code>MonadAWS</code> is a low-level interface, we could create a high-level counterpart that does precisely what our application needs:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDeploy</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">executeDeployment</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>When running our application “for real”, we would use <code>MonadAWS</code> to implement <code>MonadDeploy</code>:</p><pre><code class="pygments"><span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>The nice thing about this is we can actually test <code>executeDeploymentImpl</code> using a <code>MonadAWS</code> mock, so we can still have unit test coverage of the code on the boundaries of our system! Additionally, by containing the mock to a single place, we can test the rest of our code using a smarter fake implementation of <code>MonadDeploy</code>, helping to decouple our code from AWS’s complex API and improve the reliability and usefulness of our test suite.</p><p>They key point here is that mocking is just a small piece of the larger testing puzzle in <em>any</em> language, and that is just as true in Haskell. An overemphasis on mocking is an easy way to end up with a test suite that feels useless, probably because it is. Use mocks as a technique to insulate your application from the complexity in others’ APIs, then use more domain-specific testing techniques and type-level assertions to ensure the correctness of your logic.</p><h2><a name="how-monad-mock-works"></a>How monad-mock works</h2><p>If you’ve read this far and are convinced that monad-mock is useful, you may safely stop reading now. However, if you are interested in the details of what it actually does and what makes it tick, the rest of this blog post is going to focus on how the implementation works and how it compares to other techniques.</p><p>The centerpiece of monad-mock’s API is its monad transformer, <code>MockT</code>, which is a type constructor that accepts three types:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="p">)</span></code></pre><p>The <code>m</code> and <code>a</code> type variables obviously correspond to the usual monad transformer arguments, which represent the underlying monad and the result of the monadic computation, respectively. The <code>f</code> variable is more interesting, since it’s what makes <code>MockT</code> work at all, and it isn’t even a type: it’s a type constructor with kind <code>* -&gt; *</code>. What does it mean?</p><p>Looking at the type signature of <code>runMockT</code> gives us a little bit more information about what that <code>f</code> actually represents:</p><pre><code class="pygments"><span class="nf">runMockT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>This type signature provides two pieces of key information:</p><ol><li><p>The <code>f</code> parameter is constrained by the <code>Action f</code> constraint.</p></li><li><p>Running a mocked computation requires supplying a list of <code>WithResult f</code> values. This list corresponds to the list of expectations provided to <code>runMock</code> in earlier examples.</p></li></ol><p>To understand both of these things, it helps to examine the definition of an actual datatype that can have an <code>Action</code> instance. For the filesystem example, the action datatype looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Notice how each constructor clearly corresponds to one of the methods of <code>MonadFileSystem</code>, with a type to match. Now the purpose of the type provided to the <code>FileSystemAction</code> constructor (in this case <code>r</code>) should hopefully become clear: it represents the type of the value <em>produced</em> by each method. Also note that the type is completely phantom—it does not appear in negative position in any of the constructors.</p><p>With this in mind, we can take a look at the definition of <code>WithResult</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="p">(</span><span class="kt">:-&gt;</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span></code></pre><p>This is what defines the <code>(:-&gt;)</code> constructor from earlier in the blog post, and you can see that it effectively just represents a tuple of an action and a value of its associated result. It’s completely type-safe, since it ensures the result matches the type argument to the action.</p><p>Finally, this brings us to the <code>Action</code> class, which is not complex, but is unfortunately necessary:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">eqAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="n">showAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Notice that these methods are effectively just <code>(==)</code> and <code>show</code>, lifted to type constructors of kind <code>* -&gt; *</code>. One significant difference is that <code>eqAction</code> produces <code>Maybe (a :~: b)</code> instead of <code>Bool</code>, where <code>(:~:)</code> is from <code>Data.Type.Equality</code>. This is a type equality witness, which means a successful equality between two values allows the compiler to be sure that the two <em>types</em> are equal. This is necessary for the implementation of <code>runMockT</code> due to the phantom type in actions—in order to convince GHC that we can properly return the result of a mocked action, we need to assure it that the value we’re going to return is actually of the proper type.</p><p>Implementing this typeclass is not particularly burdensome, but it’s entirely boilerplate, so even if you want to define your own action type (that is, you don’t want to use <code>makeMock</code>), you can use the <code>deriveAction</code> function from <code>Control.Monad.Mock.TH</code> to derive an <code>Action</code> instance on an existing datatype.</p><h3><a name="connecting-the-mock-to-its-class"></a>Connecting the mock to its class</h3><p>Now that we have an action with which to mock a class, we need to actually define an instance of that class for <code>MockT</code>. For this process, monad-mock provides a <code>mockAction</code> function with the following type:</p><pre><code class="pygments"><span class="nf">mockAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span></code></pre><p>This function accepts two arguments: the name of the method being mocked and the action that represents the current call. This is easier to illustrate with an actual instance of <code>MonadFileSystem</code> using <code>MockT</code> and our <code>FileSystemAction</code> type:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MockT</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"readFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"writeFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>This allows <code>readFile</code> and <code>writeFile</code> to defer to the mock, and providing the names of the functions as strings helps monad-mock to produce useful error messages upon failure. Internally, <code>MockT</code> is a <code>StateT</code> that keeps track of a list of <code>WithResult f</code> values as its state. Each call to the mock checks the action against the internal list of calls, and if they match, it returns the associated result. Otherwise, it throws an exception.</p><p>This scheme is simple, but it seems to work remarkably well. There are some obvious enhancements that will probably be eventually necessary, like allowing action results that run in the underlying monad <code>m</code> in order to support things like <code>throwError</code> from <code>MonadError</code>, but so far, it hasn’t been necessary for what we’ve been using it for. Certain tricky signatures defy this simple technique, such as signatures where a monadic action appears in a negative position (that is, the signatures you need things like <a href="https://hackage.haskell.org/package/monad-control">monad-control</a> or <a href="https://hackage.haskell.org/package/monad-unlift">monad-unlift</a> for), but we’ve found that most of our effects don’t have any reason to include such signatures.</p><h2><a name="a-brief-comparison-with-free-r-monads"></a>A brief comparison with free(r) monads</h2><p>At this point, astute readers will likely be thinking about free monads, which parts of this technique greatly resemble. The representation of actions as GADTs is especially similar to <a href="https://hackage.haskell.org/package/freer">freer</a>, which does something extremely similar. Indeed, you can think of this technique as something that combines a freer-style representation with mtl-style classes. Given that freer already does this, you might ask yourself what the point is.</p><p>If you are already sold on free monads, monad-mock may very well be uninteresting to you. From the perspective of theoretical novelty, monad-mock is not anything new or different. However, there are a variety of practical reasons to prefer mtl over free, and it’s nice to see how easy it is to enjoy the testing benefits of free without too much extra effort.</p><p>An in-depth comparison between mtl and free is well outside the scope of this blog post. However, the key point is that this technique <em>only</em> affects test code, so the real runtime implementation will not be affected in any way. This means you can take advantage of the performance benefits and ecosystem support of mtl without sacrificing simple, expressive testing.</p><h2><a name="conclusion"></a>Conclusion</h2><p>To cap things off, I want to emphasize monad-mock’s role as a single part of a larger initiative we’ve been making for the better part of the past eighteen months. Haskell is a language with ever-evolving techniques and style, and it’s sometimes dizzying to figure out how to use all the pieces together to develop robust, maintainable applications. While monad-mock might not be anything drastically different from existing testing techniques, my hope is that it can provide an opinionated mechanism to make testing easy and accessible, even for complex interactions with other services and systems.</p><p>I’ve made an effort to make it abundantly clear in this blog post that monad-mock is <em>not</em> a silver bullet to testing, and in fact, I would prefer other techniques for ensuring correctness whenever possible. Even so, mocking is a nice tool to have in your toolbox, and it’s a good fallback to get even the worst APIs under test coverage.</p><p>If you want to try out monad-mock for yourself, <a href="https://hackage.haskell.org/package/monad-mock">take a look at the documentation on Hackage</a> and start playing around! It’s still early software, so it’s not the most proven or featureful, but we’ve managed to get mileage out of it already, all the same. If you find any problems, have a use case it does not support, or just find something about it unclear, please do not hesitate to <a href="https://github.com/cjdev/monad-mock">open an issue on the GitHub repository</a>—we obviously can’t fix issues we don’t know about.</p><p>Thanks as always to the many people who have contributed ideas that have shaped my philosophy and approach to testing and have helped provide the tools that make this library work. Happy testing!</p><ol class="footnotes"></ol></article>Realizing Hackett, a metaprogrammable Haskell2017-05-27T00:00:00Z2017-05-27T00:00:00ZAlexis King<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Lifts for free: making mtl typeclasses derivable2017-04-28T00:00:00Z2017-04-28T00:00:00ZAlexis King<article><p>Perhaps the most important abstraction a Haskell programmer must understand to effectively write modern Haskell code, beyond the level of the monad, is the <em>monad transformer</em>, a way to compose monads together in a limited fashion. One frustrating downside to monad transformers is a proliferation of <code>lift</code>s, which explicitly indicate which monad in a transformer “stack” a particular computation should run in. Fortunately, the venerable <a href="https://hackage.haskell.org/package/mtl">mtl</a> provides typeclasses that make this lifting mostly automatic, using typeclass machinery to insert <code>lift</code> where appropriate.</p><p>Less fortunately, the mtl approach does not actually eliminate <code>lift</code> entirely, it simply moves it from use sites to instances. This requires a small zoo of extraordinarily boilerplate-y instances, most of which simply implement each typeclass method using <code>lift</code>. While we cannot eliminate the instances entirely without somewhat dangerous techniques like <a href="https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#overlapping-instances">overlapping instances</a>, we <em>can</em> automatically derive them using features of modern GHC, eliminating the truly unnecessary boilerplate.</p><h2><a name="the-problem-with-mtl-style-typeclasses"></a>The problem with mtl-style typeclasses</h2><p>To understand what problem it is exactly that we’re trying to solve, we first need to take a look at an actual mtl-style typeclass. I am going to start with an mtl-<em>style</em> typeclass, rather than an actual typeclass in the mtl, due to slight complications with mtl’s actual typeclasses that we’ll get into later. Instead, let’s start with a somewhat boring typeclass, which we’ll call <code>MonadExit</code>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">System.Exit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitCode</span><span class="p">)</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is a simple typeclass that abstracts over the concept of early exit, given an exit code. The most obvious implementation of this typeclass is over <code>IO</code>, which will actually exit the program:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="k">qualified</span><span class="w"> </span><span class="nn">System.Exit</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">IO</span><span class="w"> </span><span class="p">(</span><span class="n">exitWith</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IO</span><span class="o">.</span><span class="n">exitWith</span></code></pre><p>One of the cool things about these typeclasses, though, is that we don’t have to have just one implementation. We could also write a pure implementation of <code>MonadExit</code>, which would simply short-circuit the current computation and return the <code>ExitCode</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">ExitCode</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span></code></pre><p>Instead of simply having an instance on a concrete monad, though, we probably want to be able to use this in a larger monad stack, so we can define an <code>ExitT</code> monad transformer that can be inserted into any monad transformer stack:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Except</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="p">,</span><span class="w"> </span><span class="nf">runExceptT</span><span class="p">,</span><span class="w"> </span><span class="nf">throwError</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Trans</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTrans</span><span class="p">)</span> + +<span class="nf">runExitT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">runExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span></code></pre><p>With this in place, we can write actual programs using our <code>ExitT</code> monad transformer:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">runExitT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">putStrLn</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">putStrLn</span><span class="w"> </span><span class="s">"world"</span> +<span class="nf">hello</span> +<span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>This is pretty cool! Unfortunately, experienced readers will see the rather large problem with what we have so far. Specifically, it won’t actually work if we try and wrap <code>ExitT</code> in another monad transformer:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runExitT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="n">runReaderT</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">password</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"password1234"</span><span class="p">)</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="c1">-- super secure password</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"access granted"</span> + +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"not the right password"</span> +<span class="o">&lt;</span><span class="n">interactive</span><span class="o">&gt;:</span><span class="w"> </span><span class="ne">error</span><span class="kt">:</span> +<span class="w"> </span><span class="err">•</span><span class="w"> </span><span class="kt">No</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="n">for</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">[</span><span class="kt">Char</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m0</span><span class="p">)))</span> +<span class="w"> </span><span class="n">arising</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">use</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="err">‘</span><span class="n">it</span><span class="err">’</span> +<span class="w"> </span><span class="err">•</span><span class="w"> </span><span class="kt">In</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">stmt</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">interactive</span><span class="w"> </span><span class="kt">GHCi</span><span class="w"> </span><span class="n">command</span><span class="kt">:</span><span class="w"> </span><span class="n">print</span><span class="w"> </span><span class="n">it</span></code></pre><p>The error message is relatively self-explanatory if you are familiar with mtl error messages: there is no <code>MonadExit</code> instance for <code>ReaderT</code>. This makes sense, since we only defined a <code>MonadExit</code> instance for <em><code>ExitT</code></em>, nothing else. Fortunately, the instance for <code>ReaderT</code> is completely trivial, since we just need to use <code>lift</code> to delegate to the next monad in the stack:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>Now that the delegating instance is set up, we can actually use our <code>logIn</code> function:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"not the right password"</span> +<span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"password1234"</span> +<span class="kt">Right</span><span class="w"> </span><span class="s">"access granted"</span></code></pre><h3><a name="an-embarrassment-of-instances"></a>An embarrassment of instances</h3><p>We’ve managed to make our program work properly now, but we’ve still only defined the delegating instance for <code>ReaderT</code>. What if someone wants to use <code>ExitT</code> with <code>WriterT</code>? Or <code>StateT</code>? Or any of <code>ExceptT</code>, <code>RWST</code>, or <code>ContT</code>? Well, we have to define instances for each and every one of them, and as it turns out, the instances are all identical!</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ContT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>This is bad enough on its own, but this is actually the <em>simplest</em> case: a typeclass with a single method which is trivially lifted through any other monad transformer. Another thing we’ve glossed over is actually defining all the delegating instances for the <em>other</em> mtl typeclasses on <code>ExitT</code> itself. Fortunately, we can derive these ones with <code>GeneralizedNewtypeDeriving</code>, since <code>ExceptT</code> has already done most of the work for us:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="w"> </span><span class="c1">-- base</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="c1">-- transformers-base</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTrans</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="c1">-- mtl</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadThrow</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadCatch</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadMask</span><span class="w"> </span><span class="c1">-- exceptions</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="c1">-- monad-control</span> +<span class="w"> </span><span class="p">)</span></code></pre><p>Unfortunately, we have to write the <code>MonadError</code> instance manually if we want it, since we don’t want to pick up the instance from <code>ExceptT</code>, but rather wish to defer to the underlying monad. This means writing some truly horrid delegation code:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span> + +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x&#39;</span></code></pre><p>(Notably, this is so awful because <code>catchError</code> is more complex than the simple <code>exitWith</code> method we’ve studied so far, which is why we’re starting with a simpler typeclass. We’ll get more into this later, as promised.)</p><p>This huge number of instances is sometimes referred to as the “n<sup>2</sup> instances” problem, since it requires every monad transformer have an instance of every single mtl-style typeclass. Fortunately, in practice, this proliferation is often less horrible than it might seem, mostly because deriving helps a lot. However, remember that if <code>ExitT</code> <em>weren’t</em> a simple wrapper around an existing monad transformer, we wouldn’t be able to derive the instances at all! Instead, we’d have to write them all out by hand, just like we did with all the <code>MonadExit</code> instances.</p><p>It’s a shame that these typeclass instances can’t be derived in a more general way, allowing derivation for arbitrary monad transformers instead of simply requiring the newtype deriving machinery. As it turns out, with clever use of modern GHC features, we actually <strong>can</strong>. It’s not even all that hard.</p><h2><a name="default-instances-with-default-signatures"></a>Default instances with default signatures</h2><p>It’s not hard to see that our <code>MonadExit</code> instances are all exactly the same: just <code>lift . exitWith</code>. Why is that, though? Well, every instance is an instance on a monad transformer over a monad that is already an instance of <code>MonadExit</code>. In fact, we can express this in a type signature, and we can extract <code>lift . exitWith</code> into a separate function:</p><pre><code class="pygments"><span class="nf">defaultExitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">defaultExitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>However, writing <code>defaultExitWith</code> really isn’t any easier than writing <code>lift . exitWith</code>, so this deduplication doesn’t really buy us anything. However, it <em>does</em> indicate that we could write a default implementation of <code>exitWith</code> if we could require just a little bit more from the implementing type. With <a href="https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#default-method-signatures">GHC’s <code>DefaultSignatures</code> extension</a>, we can do precisely that.</p><p>The idea is that we can write a separate type signature for a default implementation of <code>exitWith</code>, which can be more specific than the type signature for <code>exitWith</code> in general. This allows us to use our <code>defaultExitWith</code> implementation more or less directly:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DefaultSignatures #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>We have to use <code>m1</code> instead of <code>m</code>, since type variables in the instance head are always scoped, and the names would conflict. However, this creates another problem, since our specialized type signature replaces <code>m</code> with <code>t m1</code>, which won’t quite work (as GHC can’t automatically figure out they should be the same). Instead, we can use <code>m</code> in the type signature, then just add a type equality constraint ensuring that <code>m</code> and <code>t m1</code> must be the same type:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>Now we can write all of our simple instances without even needing to write a real implementation! All of the instance bodies can be empty:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ContT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>While this doesn’t completely alleviate the pain of writing instances, it’s definitely an improvement over what we had before. With <a href="https://downloads.haskell.org/~ghc/8.2.1-rc1/docs/html/users_guide/glasgow_exts.html#deriving-strategies">GHC 8.2’s new <code>DerivingStrategies</code> extension</a>, it becomes especially beneficial when defining entirely new transformers that should also have <code>ExitT</code> instances, since they can be derived with <code>DeriveAnyClass</code>:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="n">anyclass</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="p">)</span></code></pre><p>This is pretty wonderful.</p><p>Given that only <code>MonadExit</code> supports being derived in this way, we sadly still need to implement the other, more standard mtl-style typeclasses ourselves, like <code>MonadIO</code>, <code>MonadBase</code>, <code>MonadReader</code>, <code>MonadWriter</code>, etc. However, what if all of those classes provided the same convenient default signatures that our <code>MonadExit</code> does? If that were the case, then we could write something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="n">anyclass</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadThrow</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadCatch</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadMask</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span> +<span class="w"> </span><span class="p">)</span></code></pre><p>Compared to having to write all those instances by hand, this would be a pretty enormous difference. Unfortunately, many of these typeclasses are not quite as simple as our <code>MonadExit</code>, and we’d have to be a bit more clever to make them derivable.</p><h2><a name="making-mtl-s-classes-derivable"></a>Making mtl’s classes derivable</h2><p>Our <code>MonadExit</code> class was extremely simple, since it only had a single method with a particularly simple type signature. For reference, this was the type of our generic <code>exitWith</code>:</p><pre><code class="pygments"><span class="nf">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Let’s now turn our attention to <code>MonadReader</code>. At first blush, this typeclass should not be any trickier to implement than <code>MonadExit</code>, since the types of <code>ask</code> and <code>reader</code> are both quite simple:</p><pre><code class="pygments"><span class="nf">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="nf">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>However, the type of the other method, <code>local</code>, throws a bit of a wrench in our plans. It has the following type signature:</p><pre><code class="pygments"><span class="nf">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Why is this so much more complicated? Well, the key is in the second argument, which has the type <code>m a</code>. That’s not something that can be simply <code>lift</code>ed away! Try it yourself: try to write a <code>MonadReader</code> instance for some monad transformer. It’s not as easy as it looks!</p><p>We can illustrate the problem by creating our own version of <code>MonadReader</code> and implementing it for something like <code>ExceptT</code> ourselves. We can start with the trivial methods first:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span></code></pre><p>However, implementing <code>local</code> is harder. Let’s specialize the type signature to <code>ExceptT</code> to make it more clear why:</p><pre><code class="pygments"><span class="nf">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Our base monad, <code>m</code>, implements <code>local</code>, but we have to convert the first argument from <code>ExceptT e m a</code> into <code>m (Either e a)</code> first, run it through <code>local</code> in <code>m</code>, then wrap it back up in <code>ExceptT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>This operation is actually a mapping operation of sorts, since we’re mapping <code>local f</code> over <code>x</code>. For that reason, this can be rewritten using the <code>mapExceptT</code> function provided from <code>Control.Monad.Except</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapExceptT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">local</span></code></pre><p>If you implement <code>MonadReader</code> instances for other transformers, like <code>StateT</code> and <code>WriterT</code>, you’ll find that the instances are exactly the same <em>except</em> for <code>mapExceptT</code>, which is replaced with <code>mapStateT</code> and <code>mapWriterT</code>, respectively. This is sort of obnoxious, given that we want to figure out how to create a generic version of <code>local</code> that works with any monad transformer, but this requires concrete information about which monad we’re in. Obviously, the power <code>MonadTrans</code> gives us is not enough to make this generic. Fortunately, there is a typeclass which does: <a href="http://hackage.haskell.org/package/monad-control-1.0.1.0/docs/Control-Monad-Trans-Control.html#t:MonadTransControl"><code>MonadTransControl</code></a> from the <code>monad-control</code> package.</p><p>Using <code>MonadTransControl</code>, we can write a generic <code>mapT</code> function that maps over an arbitrary monad transformer with a <code>MonadTransControl</code> instance:</p><pre><code class="pygments"><span class="nf">mapT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="p">),</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span> +<span class="nf">mapT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">return</span></code></pre><p>This type signature may look complicated (and, well, it is), but the idea is that the <code>StT</code> associated type family encapsulates the monadic state that <code>t</code> introduces. For example, for <code>ExceptT</code>, <code>StT (ExceptT e) a</code> is <code>Either e a</code>. For <code>StateT</code>, <code>StT (StateT s) a</code> is <code>(a, s)</code>. Some transformers, like <code>ReaderT</code>, have no state, so <code>StT (ReaderT r) a</code> is just <code>a</code>.</p><p>I will not go into the precise mechanics of how <code>MonadTransControl</code> works in this blog post, but it doesn’t matter significantly; the point is that we can now use <code>mapT</code> to create a generic implementation of <code>local</code> for use with <code>DefaultSignatures</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> + +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">local</span> + +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">ask</span></code></pre><p>Once more, we now get instances of our typeclass, in this case <code>MonadReader</code>, <strong>for free</strong>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>It’s also worth noting that we <em>don’t</em> get a <code>ContT</code> instance for free, even though <code>ContT</code> has a <code>MonadReader</code> instance in mtl. Unlike the other monad transformers mtl provides, <code>ContT</code> does not have a <code>MonadTransControl</code> instance because it cannot be generally mapped over. While a <code>mapContT</code> function does exist, its signature is more restricted:</p><pre><code class="pygments"><span class="nf">mapContT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ContT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ContT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>It happens that <code>local</code> can still be implemented for <code>ContT</code>, so it can still have a <code>MonadReader</code> instance, but it cannot be derived in the same way as it can for the other transformers. Still, in practice, I’ve found that most user-defined transformers do not have such complex control flow, so they can safely be instances of <code>MonadTransControl</code>, and they get this deriving for free.</p><h3><a name="extending-this-technique-to-other-mtl-typeclasses"></a>Extending this technique to other mtl typeclasses</h3><p>The default instances for the other mtl typeclasses are slightly different from the one for <code>MonadReader</code>, but for the most part, the same general technique applies. Here’s a derivable <code>MonadError</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span> + +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">f</span><span class="p">))</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">return</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>The <code>MonadState</code> interface turns out to be extremely simple, so it doesn’t even need <code>MonadTransControl</code> at all:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">get</span> + +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">put</span> + +<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>Everything seems to be going well! However, not everything is quite so simple.</p><h3><a name="a-monadwriter-diversion"></a>A <code>MonadWriter</code> diversion</h3><p>Unexpectedly, <code>MonadWriter</code> turns out to be by far the trickiest of the bunch. It’s not too hard to create default implementations for most of the methods of the typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="p">(</span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">writer</span> + +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tell</span> + +<span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="n">y&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="p">(</span><span class="n">return</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">y&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span></code></pre><p>However, <code>MonadWriter</code> has a fourth method, <code>pass</code>, which has a particularly tricky type signature:</p><pre><code class="pygments"><span class="nf">pass</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>As far as I can tell, this is not possible to generalize using <code>MonadTransControl</code> alone, since it would require inspection of the result of the monadic argument (that is, it would require a function from <code>StT t (a, b) -&gt; (StT t a, b)</code>), which is not possible in general. My gut is that this could likely also be generalized with a slightly more powerful abstraction than <code>MonadTransControl</code>, but it is not immediately obvious to me what that abstraction should be.</p><p>One extremely simple way to make this possible would be to design something to serve this specific use case:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">RunSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="o">.</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="kt">RunSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Instances of <code>MonadTransSplit</code> would basically just provide a way to pull out bits of the result, if possible:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">Nothing</span><span class="p">)</span> +<span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">),</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">),</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span></code></pre><p>Then, using this, it would be possible to write a generic version of <code>pass</code>:</p><pre><code class="pygments"><span class="kr">default</span><span class="w"> </span><span class="n">pass</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">pass</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pass</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="kr">case</span> +<span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">f</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Nothing</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="p">(</span><span class="n">return</span><span class="w"> </span><span class="n">r</span><span class="p">)</span></code></pre><p>However, this seems pretty overkill for just one particular method, given that I have no idea if <code>MonadTransSplit</code> would be useful <em>anywhere</em> else. One interesting thing about going down this rabbit hole, though, is that I learned that <code>pass</code> has some somewhat surprising behavior when mixed with transformers like <code>ExceptT</code> or <code>MaybeT</code>, if you don’t carefully consider how it works. It’s a strange method with a somewhat strange interface, so I don’t think I have a satisfactory conclusion about <code>MonadWriter</code> yet.</p><h2><a name="regrouping-and-stepping-back"></a>Regrouping and stepping back</h2><p>Alright, that was a lot of fairly intense, potentially confusing code. What the heck did we actually accomplish? Well, we got a couple of things:</p><ol><li><p>First, we developed a technique for writing simple mtl-style typeclasses that are derivable using <code>DeriveAnyClass</code> (or simply writing an empty instance declaration). We used a <code>MonadExit</code> class as a proof of concept, but really, the technique is applicable to most mtl-style typeclasses that represent simple effects (including, for example, <code>MonadIO</code>).</p><p>This technique is useful in isolation, even if you completely disregard the rest of the blog post. For an example where I recently applied it in real code, see <a href="https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L226-L271">the default signatures provided with <code>MonadPersist</code> from the <code>monad-persist</code> library</a>, which make <a href="https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L506-L513">defining instances completely trivial</a>. If you use mtl-style typeclasses in your own application to model effects, I don’t see much of a reason <em>not</em> to use this technique.</p></li><li><p>After <code>MonadExit</code>, we applied the same technique to the mtl-provided typeclasses <code>MonadReader</code>, <code>MonadError</code>, and <code>MonadState</code>. These are a bit trickier, since the first two need <code>MonadTransControl</code> in addition to the usual <code>MonadTrans</code>.</p><p>Whether or not this sort of thing should actually be added to mtl itself probably remains to be seen. For the simplest typeclass, <code>MonadState</code>, it seems like there probably aren’t many downsides, but given the difficulty implementing it for <code>MonadWriter</code> (or, heaven forbid, <code>MonadCont</code>, which I didn’t even seriously take a look at for this blog post), it doesn’t seem like an obvious win. Consistency is important.</p><p>Another downside that I sort of glossed over is possibly even more significant from a practical point of view: adding default signatures to <code>MonadReader</code> would require the removal of the default implementation of <code>ask</code> that is provided by the existing library (which implements <code>ask</code> in terms of <code>reader</code>). This would be backwards-incompatible, so it’d be difficult to change, even if people wanted to do it. Still, it’s interesting to consider what these typeclasses might look like if they were designed today.</p></li></ol><p>Overall, these techniques are not a silver bullet for deriving mtl-style typeclasses, nor do they eliminate the n<sup>2</sup> instances problem that mtl style suffers from. That said, they <em>do</em> significantly reduce boilerplate and clutter in the simplest cases, and they demonstrate how modern Haskell’s hierarchy of typeclasses provides a lot of power, both to describe quite abstract concepts and to alleviate the need to write code by hand.</p><p>I will continue to experiment with the ideas described in this blog post, and I’m sure some more pros and cons will surface as I explore the design space. If you have any suggestions for how to deal with “the <code>MonadWriter</code> problem”, I’d be very interested to hear them! In the meantime, consider using the technique in your application code when writing effectful, monadic typeclasses.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questions2017-01-05T00:00:00Z2017-01-05T00:00:00ZAlexis King<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheses2017-01-02T00:00:00Z2017-01-02T00:00:00ZAlexis King<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Using types to unit-test in Haskell2016-10-03T00:00:00Z2016-10-03T00:00:00ZAlexis King<article><p>Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.</p><p>Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.</p><h2><a name="first-an-aside-on-testing-philosophy"></a>First, an aside on testing philosophy</h2><p>Testing methodology is a controversial topic within the larger programming community, and there are a multitude of different approaches. This blog post is about <em>unit testing</em>, an already nebulous term with a number of different definitions. For the purposes of this post, I will define a unit test as a test that stubs out collaborators of the code under test in some way. Accomplishing that in Haskell is what this is primarily about.</p><p>I want to be clear that I do not think that unit tests are the only way to write tests, nor the best way, nor even always an applicable way. Depending on your domain, rigorous unit testing might not even make sense, and other forms of tests (end-to-end, integration, benchmarks, etc.) might fulfill your needs.</p><p>In practice, though, implementing those other kinds of tests seems to be well-documented in Haskell compared to pure, object-oriented style unit testing. As my Haskell applications have grown, I have found myself wanting a more fine-grained testing tool that allows me to both test a piece of my codebase in isolation and also use my domain-specific types. This blog post is about that.</p><p>With that disclaimer out of the way, let’s talk about testing in Haskell.</p><h2><a name="drawing-seams-using-types"></a>Drawing seams using types</h2><p>One of the primary attributes of unit tests in object-oriented languages, especially statically-typed ones, is the concept of “seams” within a codebase. These are internal boundaries between components of a system. Some boundaries are obvious—interactions with a database, manipulation of the file system, and performing I/O over the network, to name a few examples—but others are more subtle. Especially in larger codebases, it can be helpful to isolate two related but distinct pieces of functionality as much as possible, which makes them easier to reason about, even if they’re actually part of the same codebase.</p><p>In OO languages, these seams are often marked using interfaces, whether explicitly (in the case of static languages) or implicitly (in the case of dynamic ones). By programming to an interface, it’s possible to create “fake” implementations of that interface for use in unit tests, effectively making it possible to stub out code that isn’t directly relevant to the code being tested.</p><p>In Haskell, representing these seams is a lot less obvious. Consider a fairly trivial function that reverses a file’s contents on the file system:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span></code></pre><p>This function is impossible to test without testing against a real file system. It simply performs I/O directly, and there’s no way to “mock out” the file system for testing purposes. Now, admittedly, this function is so trivial that a unit test might not seem worth the cost, but consider a slightly more complicated function that interacts with a database:</p><pre><code class="pygments"><span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>It might now be a bit more clear that it could be useful to test the above function without running a real database and doing all the necessary context setup before each test case. Indeed, it would be nice if a test could just provide stubbed implementations for <code>fetchUser</code> and <code>fetchRecentPosts</code>, then make assertions about the output.</p><p>One way to solve this problem is to pass the results of those two functions to <code>renderUserProfile</code> as arguments, turning it into a pure function that could be easily tested. This becomes obnoxious for functions of even just slightly more complexity, though (it is not unreasonable to imagine needing a handful of different queries to render a user’s profile page), and it requires significantly restructuring code simply because the tests need it.</p><p>The above code is not only difficult to test, however—it has another problem, too. Specifically, both functions return <code>IO</code> values, which means they can effectively do <em>anything</em>. Haskell has a very strong type system for typing terms, but it doesn’t provide any guarantees about effects beyond a simple yes/no answer about function purity. Even though the <code>renderUserProfile</code> function should really only need to interact with the database, it could theoretically delete files, send emails, make HTTP requests, or do any number of other things.</p><p>Fortunately, it’s possible to solve <em>both</em> problems—a lack of testability and a lack of type safety—using the same general technique. This approach is reminiscent of the interface-based seams of object-oriented languages, but unlike most object-oriented approaches, it provides additional type safety guarantees without the need to explicitly modify the code to support some kind of dependency injection.</p><h3><a name="making-implicit-interfaces-explicit"></a>Making implicit interfaces explicit</h3><p>Statically typed, object-oriented languages provide interfaces as a language construct to encode certain kinds of contracts into the type system, and Haskell has something similar. Typeclasses are, in many ways, an analog to OO interfaces, and they can be used in a similar way. In the above case, let’s write down interfaces that the <code>reverseFile</code> and <code>renderUserProfile</code> functions can use:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span></code></pre><p>The really nice thing about these interfaces is that our function implementations don’t have to change <em>at all</em> to take advantage of them. In fact, all we have to change is their types:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span> + +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty neat, since we haven’t had to alter our code at all, but we’ve managed to completely decouple ourselves from <code>IO</code>. This has the direct effect of both making our code more abstract (we no longer rely on the “real” file system or a “real” database, which makes our code easier to test) and restricting what our functions can do (just from looking at the type signatures, we know what side-effects they can perform).</p><p>Of course, since we’re now coding against an interface, our code doesn’t actually do much of anything. If we want to actually use the functions we’ve written, we’ll have to define instances of <code>MonadFS</code> and <code>MonadDB</code>. When actually running our code, we’ll probably still use <code>IO</code> (or some monad transformer stack with <code>IO</code> at the bottom), so we can define trivial instances for that existing use case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchUser</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchRecentPosts</span></code></pre><p>Even if we go no further, <strong>this is already incredibly useful</strong>. By restricting the sorts of effects our functions can perform at the type level, it becomes a lot easier to see which code is interacting with what. This can be invaluable when working in a part of a moderately large codebase that you are unfamiliar with. Even if the only instance of these typeclasses is <code>IO</code>, the benefits are immediately apparent.</p><p>Of course, this blog post is about testing, so we’re going to go further and take advantage of these seams we’ve now drawn. The question is: how?</p><h2><a name="testing-with-typeclasses-an-initial-attempt"></a>Testing with typeclasses: an initial attempt</h2><p>Given that we now have functions depending on an interface instead of <code>IO</code>, we can create separate instances of our typeclasses for use in tests. Let’s start with the <code>renderUserProfile</code> function. We’ll create a simple wrapper around the <code>Identity</code> type, since we don’t actually care much about the “effects” of our <code>MonadDB</code> methods:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Functor.Identity</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">unTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now, we’ll create a trivial instance of <code>MonadDB</code> for <code>TestM</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa"</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">Post</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">postTitle</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span></code></pre><p>With this instance, it’s now possible to write a simple unit test of the <code>renderUserProfile</code> function that doesn’t need a real database running at all:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"renderUserProfile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows the user’s name"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="s">"Alyssa’s Profile"</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows a list of the user’s posts"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">li</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty nice, and running the above tests reveals a nice property of these kinds of isolated test cases: the test suite runs <em>really, really fast</em>. Communicating with a database, even in extremely simple ways, takes a measurable amount of time, especially with dozens of tests. In contrast, even with hundreds of tests, our unit test suite runs in less than a tenth of a second.</p><p>This all seems to be successful, so let’s try and apply the same testing technique to <code>reverseFile</code>.</p><h3><a name="testing-side-effectful-code"></a>Testing side-effectful code</h3><p>Looking at the type signature for <code>reverseFile</code>, we have a small problem:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Specifically, the return type is <code>()</code>. Making any assertions against the result of this function would be completely worthless, given that it’s guaranteed to be the same exact thing each time. Instead, <code>reverseFile</code> is inherently side-effectful, so we want to be able to test that it properly interacts with the file system in the correct way.</p><p>In order to do this, a simple wrapper around <code>Identity</code> won’t be enough, but we can replace it with something more powerful: <code>Writer</code>. Specifically, we can use a writer monad to “log” what gets called in order to test side-effects. We’ll start by creating a new <code>TestM</code> type, just like last time:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">])</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span></code></pre><p>Using this slightly more powerful type, we can write a useful instance of <code>MonadFS</code> that will track the argument given to <code>writeFile</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span></code></pre><p>Again, the instance is quite simple, but it now enables us to write a straightforward unit test for <code>reverseFile</code>:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span></code></pre><p>Again, quite simple to both implement and use, and the test itself is blindingly fast. There’s another problem, though, which is that we have technically left part of <code>reverseFile</code> untested: we’ve completely ignored the <code>path</code> argument.</p><p>In this contrived example, it may seem silly to test something so trivial, but in real code, it’s quite possible that one would care very much about testing multiple different aspects about a single function. When testing <code>renderUserProfile</code>, this was not hard, since we could reuse the same <code>TestM</code> type and <code>MonadDB</code> instance for both test cases, but in the <code>reverseFile</code> example, we’ve ignored the path entirely.</p><p>We <em>could</em> adjust our <code>MonadFS</code> instance to also track the path provided to each method, but this has a few problems. First, it means every test case would depend on all the various properties we are testing, which would mean updating every test case when we add a new one. It would also be simply impossible if we needed to track multiple types—in this particular case, it turns out that <code>String</code> and <code>FilePath</code> are actually the same type, but in practice, there may be a handful of disparate, incompatible types.</p><p>Both of the above issues could be fixed by creating a sum type and manually filtering out the relevant elements in each test case, but a much more intuitive approach would be to simply have a separate instance for each case. Unfortunately, in Haskell, creating a new instance means creating an entirely new type. To illustrate how much duplication that would entail, we could create the following type and instance for testing proper propagation of the <code>path</code> argument:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">])</span> + +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span></code></pre><p>Now it’s possible to add an extra test case that asserts that the proper path is provided to the two filesystem functions:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This works, but it’s ultimately unacceptably complicated. Our test harness code is now significantly larger than the actual tests themselves, and the amount of boilerplate is frustrating. Verbose test suites are especially bad, since forcing programmers to jump through hoops just to implement a single test reduces the likelihood that people will actually write good tests, if they write tests at all. In contrast, if writing tests is easy, then people will naturally write more of them.</p><p>The above strategy to writing tests is not good enough, but it does reveal a particular problem: in Haskell, typeclass instances are not first-class values that can be manipulated and abstracted over, they are static constructs that can only be managed by the compiler, and users do not have a direct way to modify them. With some cleverness, however, we can actually create an approximation of first-class typeclass dictionaries, which will allow us to dramatically simplify the above testing mechanism.</p><h2><a name="creating-first-class-typeclass-instances"></a>Creating first-class typeclass instances</h2><p>In order to provide an easy way to construct instances, we need a way to represent instances as ordinary Haskell values. This is not terribly difficult, given that instances are conceptually just records containing a collection of functions. For example, we could create a datatype that represents an instance of the <code>MonadFS</code> typeclass:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>To avoid namespace clashes with the actual method identifiers, the record fields are prefixed with an underscore, but otherwise, the translation is remarkably straightforward. Using this record type, we can easily create values that represent the two instances we defined above:</p><pre><code class="pygments"><span class="nf">contentInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> + +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>These two values represent two different implementations of <code>MonadFS</code>, but since they’re ordinary Haskell values, they can be manipulated and even <em>extended</em> like any other records. This can be extremely useful, since it makes it possible to create a sort of “base” instance, then have individual test cases override individual pieces of functionality piecemeal.</p><p>Of course, although we’ve written these two instances, we have no way to actually use them. After all, Haskell does not provide a way to explicitly provide typeclass dictionaries. Fortunately, we can create a sort of “proxy” type that will use a reader to thread the dictionary around explicitly, and the instance can defer to the dictionary’s implementation.</p><h3><a name="creating-an-instance-proxy"></a>Creating an instance proxy</h3><p>To represent our proxy type, we’ll use a combination of a <code>Writer</code> and a <code>ReaderT</code>; the former to implement the logging used by instances, and the latter to actually thread around the dictionary. Our type will look like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">log</span> +<span class="w"> </span><span class="p">)</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="n">inst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">inst</span><span class="p">)</span></code></pre><p>This might look rather complicated, and it is, but let’s break down exactly what it’s doing.</p><ol><li><p>The <code>TestM</code> type includes two type parameters. The first is the type of value that will be logged (hence the name <code>log</code>), which corresponds to the argument to <code>Writer</code> from previous incarnations of <code>TestM</code>. Unlike those types, though, we want this version to work with any <code>Monoid</code>, so we’ll make it a type parameter. The second parameter is simply the type of the current monadic value, as before.</p></li><li><p>The type itself is defined as a wrapper around a small monad transformer stack, the first of which is <code>ReaderT</code>. The state threaded around by the reader is, in this case, the instance dictionary, which is <code>MonadFSInst</code>.</p><p>However, recall that <code>MonadFSInst</code> accepts a type variable—the type of a monad itself—so we must provide <code>TestM log</code> as an argument to <code>MonadFSInst</code>. This slight bit of indirection allows us to tie the knot between the mutually dependent instances and proxy type.</p></li><li><p>The base monad in the transformer stack is <code>Writer</code>, which is used to actually implement the logging functionality, just like in prior cases. The only difference now is that the <code>log</code> type parameter now determines what the writer actually produces.</p></li><li><p>Finally, as before, we use <code>GeneralizedNewtypeDeriving</code> to derive all the relevant <code>mtl</code> classes, adding the somewhat wordy <code>MonadReader</code> constraint to the list.</p></li></ol><p>Using this single type, we can now implement a <code>MonadFS</code> instance that defers to the dictionary carried around within <code>TestM</code>’s reader state:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_readFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_writeFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This may seem somewhat boilerplate-y, and it is to some extent, but the important consideration is that this boilerplate only needs to be written <em>once</em>. With this in place, it’s now possible to write an arbitrary number of first-class instances that use the above mechanism without extending the mechanism at all.</p><p>To see what actually using this code would look like, let’s update the <code>reverseFile</code> tests to use the new <code>TestM</code> implementation, as well as the <code>contentInst</code> and <code>pathInst</code> dictionaries from earlier:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>We can do a little bit better, though. Really, the definitions of <code>contentInst</code> and <code>pathInst</code> are specific to each test case. With ordinary typeclass instances, we cannot scope them to any particular block, but since <code>MonadFSInst</code> is just an ordinary Haskell datatype, we can manipulate them just like any other Haskell values. Therefore, we can just inline those instances’ definitions into the test cases themselves to keep them closer to the actual tests.</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This is pretty good. We’re now able to create inline instances of our <code>MonadFS</code> typeclass, which allows us to write extremely concise unit tests using ordinary Haskell typeclasses as system seams. We’ve managed to cut down on the boilerplate considerably, though we still have a couple problems. For one, this example only uses a single typeclass containing only two methods. A real <code>MonadFS</code> typeclass would likely have at least a dozen methods for performing various filesystem operations, and writing out the instance dictionaries for every single method, even the ones that aren’t used within the code under test, would be pretty frustratingly verbose.</p><p>This problem is solvable, though. Since instances are just ordinary Haskell records, we can create a “base” instance that just throws an exception whenever the method is called:</p><pre><code class="pygments"><span class="nf">baseInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">baseInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_readFile’"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_writeFile’"</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Then code that only uses <code>readFile</code> could only override that particular method, for example:</p><pre><code class="pygments"><span class="kr">let</span><span class="w"> </span><span class="n">myInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">baseInst</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span></code></pre><p>Normally, of course, this would be a terrible idea. However, since this is all just test code, it can be extremely useful in quickly figuring out what methods need to be stubbed out for a particular test case. Since all the code actually gets run at test time, attempts to use unimplemented instance methods will immediately raise an error, informing the programmer which methods need to be implemented to make the test pass. This can also help to significantly cut down on the amount of effort it takes to implement each test.</p><p>Another problem is that our approach is specialized exclusively to <code>MonadFS</code>. What about functions that use both <code>MonadFS</code> <em>and</em> <code>MonadDB</code>, for example? Fortunately, that is not hard to solve, either. We can adapt the <code>MonadFSInst</code> type to include fields for all of the typeclasses relevant to our system, turning it into a generic test fixture of sorts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FixtureInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FixtureInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">-- MonadFS</span> +<span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="c1">-- MonadDB</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Updating <code>TestM</code> to use <code>FixtureInst</code> instead of <code>MonadFSInst</code> is trivial, and all the rest of the infrastructure still works. However, this means that every time a new typeclass is added, three things need to be updated:</p><ol><li><p>Its methods need to be added to the <code>FixtureInst</code> record.</p></li><li><p>Those methods need to be given error-raising defaults in the <code>baseInst</code> value.</p></li><li><p>An actual instance of the typeclass needs to be written for <code>TestM</code> that defers to the <code>FixtureInst</code> value.</p></li></ol><p>Furthermore, most of this manual manipulation of methods is required every time a particular typeclass changes, whether that means adding a method, removing a method, renaming a method, or changing a method’s type. This is especially frustrating given that all this code is really just mechanical boilerplate that could all be derived by the set of typeclasses being tested.</p><p>That last point is especially important: aside from the instances themselves, every piece of boilerplate above is obviously possible to generate from existing types alone. With that piece of information in mind, we can do even better: we can use Template Haskell.</p><h2><a name="removing-the-boilerplate-using-test-fixture"></a>Removing the boilerplate using <code>test-fixture</code></h2><p>The above code was not only rather boilerplate-heavy, it was pretty complicated. Fortunately, you don’t actually have to write it. Enter the library <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code></a>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture.TH</span> + +<span class="nf">mkFixture</span><span class="w"> </span><span class="s">"FixtureInst"</span><span class="w"> </span><span class="p">[</span><span class="kt">&#39;&#39;MonadFS</span><span class="p">,</span><span class="w"> </span><span class="kt">&#39;&#39;MonadDB</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">contentInst</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">pathInst</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p><strong>That’s it.</strong> The above code automatically generates everything you need to write fast, simple, deterministic unit tests in Haskell. The <code>mkFixture</code> function is a Template Haskell macro that expands into a definition quite similar to the <code>FixtureInst</code> type we wrote by hand, but since it’s automatically generated from the typeclass definitions, it never needs to be updated.</p><p>The <code>logTestFixture</code> function replaces the <code>logTestM</code> function we wrote by hand, but it works exactly the same. The <code>Control.Monad.TestFixture</code> library also exports a <code>log</code> function that is a synonym for <code>tell . singleton</code>, but using <code>tell</code> directly still works if you prefer.</p><p>The <code>mkFixture</code> function also generates a <code>Default</code> instance, which replaces the <code>baseInst</code> value defined earlier. It functions the same way, though, producing useful error messages that refer to the names of unimplemented typeclass methods that have not been stubbed out.</p><p>This blog post is not a <code>test-fixture</code> tutorial—indeed, it is much more complicated than a <code>test-fixture</code> tutorial would be, since it covers what the library is really doing under the hood—but if you’re interested, I would highly recommend you take a look at the <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code> documentation on Hackage</a>.</p><h2><a name="conclusion-credits-and-similar-techniques"></a>Conclusion, credits, and similar techniques</h2><p>This blog post came about as the result of a need my coworkers and I found when writing Haskell code; we wanted a way to write unit tests quickly and easily, but we didn’t find much advice from the rest of the Haskell ecosystem. The <code>test-fixture</code> library is the result of that exploratory work, and we currently use it to test a significant portion of our Haskell code.</p><p>It would be extremely unfair to suggest that I was the inventor of this technique or the inventor of the library. Two of my coworkers, <a href="https://github.com/jxv">Joe Vargas</a> and <a href="https://github.com/aztecrex">Greg Wiley</a>, came up with the general approach and wrote <code>Control.Monad.TestFixture</code>, and I simply wrote the Template Haskell macro to eliminate the boilerplate. With that in mind, I think I can say with some fairness that I think this technique is a joy to use when unit testing is a desirable goal, and I would definitely recommend it if you are interested in doing isolated testing in Haskell.</p><p>The general technique of using typeclasses to emulate effects was in part inspired by the well-known <code>mtl</code> library. An alternate approach to writing unit-testable Haskell code is using free monads, but overall, I prefer this approach over free monads because the typeclass constraints add type safety in ways that free monads do not (at least not without additional boilerplate), and this approach also lends itself well to static analysis-based boilerplate reduction techniques. It has its own tradeoffs, though, so if you’ve had success with free monads, then I certainly make no claim this is a superior approach, just one that I’ve personally found pleasant.</p><p>As a final note, if you <em>do</em> check out <code>test-fixture</code>, feel free to leave feedback by opening issues on <a href="https://github.com/cjdev/test-fixture/issues">the GitHub issue tracker</a>—even things like confusing documentation are worth a bug report.</p><ol class="footnotes"></ol></article>Climbing the infinite ladder of abstraction2016-08-11T00:00:00Z2016-08-11T00:00:00ZAlexis King<article><p>I started programming in elementary school.</p><p>When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to <a href="https://xkcd.com/974/">solve the general problem</a>. When I learned about programming, I was immediately hooked: it was <em>so easy</em> to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.</p><p>Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of <strong>abstraction</strong>.</p><h2><a name="the-brick-wall-of-inexpressiveness"></a>The brick wall of inexpressiveness</h2><p>When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:</p><pre><code class="pygments"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="p">;</span> +<span class="p">}</span> + +<span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span> +<span class="p">}</span></code></pre><p>This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.</p><p>It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The <em>whole reason</em> I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.</p><p>When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.</p><p>Both of these languages were flawed, as all languages are, but they opened my mind to the idea that <em>programming languages</em> could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.</p><h2><a name="discovering-lisp"></a>Discovering Lisp</h2><p>Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.</p><p>This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?</p><p>The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a <a href="https://github.com/lexi-lambda/libsol">highly dynamic Lisp with a prototypal object system called Sol</a>. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.</p><p>Unfortunately, it was also abysmally slow.</p><p>I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called <a href="http://racket-lang.org">Racket</a>. At about the same time, someone pointed me to a totally different language called <a href="https://www.haskell.org">Haskell</a>. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.</p><h2><a name="a-journey-into-complexity"></a>A journey into complexity</h2><p>Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned <em>so much more</em> since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.</p><p>I’ve also started realizing something else, though: <strong>the languages I’ve settled into are <em>really complicated</em>.</strong></p><p>When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.</p><p>Why?</p><p>Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”</p><p>Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?</p><p>I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.</p><p>Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I <em>still</em> have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?</p><h3><a name="improvement-but-never-mastery"></a>Improvement, but never mastery</h3><p>Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.</p><p>For example I am pretty confident that I <em>get</em> JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques <em>within</em> the language, not entirely new linguistic constructs.</p><p>On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about <code>TypeInType</code>? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do <em>not</em> have a good grasp on all the intricacies of Racket’s macro system.</p><p>This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out <code>DataKinds</code>, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.</p><p>Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is <em>terrible</em>, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.</p><p>Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even <em>more</em> advanced things, and still, none of them truly solve my problems.</p><h2><a name="ultimately-it-all-has-at-least-a-little-value"></a>Ultimately, it all has (at least a little) value</h2><p>It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.</p><p>The scariest part of all is that I think it’s completely worthwhile.</p><p>So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs <em>seem</em> unnecessarily complicated, they <em>seem</em> increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.</p><p>The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.</p><p>Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:</p><pre><code class="pygments"><span class="nl">10</span><span class="w"> </span><span class="kr">INPUT</span><span class="w"> </span><span class="s2">"What is your name: "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span> +<span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">"Hello "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span></code></pre><p>On the other hand, very few would probably understand this one:</p><pre><code class="pygments"><span class="c1">-- | A class for categories.</span> +<span class="c1">-- id and (.) must form a monoid.</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">Category</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="c1">-- | the identity morphism</span> +<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="c1">-- | morphism composition</span> +<span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">c</span></code></pre><p>Yet very few new programs are being written in BASIC, and lots are being written in Haskell.</p><p>Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:</p><pre><code class="pygments"><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composeWithProps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">curry</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">childProps</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">omit</span><span class="p">([</span><span class="s1">&#39;children&#39;</span><span class="p">],</span><span class="w"> </span><span class="nx">childProps</span><span class="p">),</span><span class="w"> </span><span class="nx">childProps</span><span class="p">.</span><span class="nx">children</span><span class="p">));</span> +<span class="w"> </span><span class="c1">// give the composed component a pretty display name for debugging</span> +<span class="w"> </span><span class="nx">composed</span><span class="p">.</span><span class="nx">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`Composed(</span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="si">}</span><span class="sb">, </span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">composed</span><span class="p">;</span> +<span class="p">});</span></code></pre><p>Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.</p><p>That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an <em>extremely</em> productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.</p><h3><a name="reactionary-anti-intellectualism-and-the-search-for-moderation"></a>Reactionary anti-intellectualism and the search for moderation</h3><p>I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. <a href="https://twitter.com/lexi_lambda/status/763111451691134976">I recently tweeted a picture of some rather dense mathematics from a paper I’d read</a>, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.</p><p>Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like <code>map</code> should be banned because they are too confusing and not immediately self-explanatory.</p><p>I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.</p><p>Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern <em>technology</em> possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.</p><p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.</p><p>I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.</p><p>Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.</p><ol class="footnotes"></ol></article>Four months with Haskell2016-06-12T00:00:00Z2016-06-12T00:00:00ZAlexis King<article><p>At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.</p><p>Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an <em>incredible</em> programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.</p><h2><a name="dispelling-some-myths"></a>Dispelling some myths</h2><p>Before moving on and discussing my experiences in depth, I want to take a quick detour to dispel some frequent rumors I hear about why Haskell is at least potentially problematic. These are things I hear a <em>lot</em>, and nothing in my experience so far would lead me to believe these are actually true. Ultimately, I don’t want to spend too much time on these—I think that, for the most part, they are nitpicks that people complain about to avoid understanding the deeper and more insidious problems with the language—but I think it’s important to at least mention them.</p><h3><a name="hiring-haskell-developers-is-not-hard"></a>Hiring Haskell developers is not hard</h3><p>I am on the first Haskell team in my company, and I am among the first Haskell developers we ever hired. Not only were we hiring without much experience with Haskell at all, we explicitly <em>did not</em> want to hire remote. Debate all you like about whether or not permitting remote work is a good idea, but I don’t think anyone would dispute that this constraint makes hiring much harder. We didn’t have any trouble finding a very large stream of qualified applicants, and it definitely seems to have dispelled any fears that we would have trouble finding new candidates in the future.</p><h3><a name="performing-i-o-in-haskell-is-easy"></a>Performing I/O in Haskell is easy</h3><p>Haskell’s purity is a point of real contention, and it’s one of the most frustrating complaints I often hear about Haskell. It is surprisingly common to hear concerns along the lines of “I don’t want to use Haskell because its academic devotion to purity sounds like it would make it very hard to get anything done”. There are very valid reasons to avoid Haskell, but in practice, I/O is not one of them. In fact, I found that isolating I/O in Haskell was much the same as isolating I/O in every other language, which I need to do anyway to permit unit testing.</p><p>...you <em>do</em> write deterministic unit tests for your impure logic, right?</p><h3><a name="working-with-lots-of-monads-is-not-very-difficult"></a>Working with lots of monads is not very difficult</h3><p>The “M word” has ended up being a running joke <em>about</em> Haskell that actually ends up coming up fairly rarely <em>within</em> the Haskell community. To be clear, there is <em>no doubt</em> in my mind that monads make Haskell intimidating and provide a steep learning curve for new users. The proliferation of the joke that monads are impossible to explain, to the point of becoming mythologized, is absolutely indicative of a deeper problem about Haskell’s accessibility. However, once people learn the basics about monads, I’ve found that applying them is just as natural as applying any other programming pattern.</p><p>Monads are used to assist the programmer, not impede them, and they really do pay off in practice. When something has a monadic interface, there’s a decent chance I already know what that interface is going to do, and that makes working with lots of different monads surprisingly easy. Admittedly, I do rely very, very heavily on tooling to help me out here, but with things like mouseover type tooltips, I’ve actually found that working with a variety of different monads and monad transformers is actually quite pleasant, and it makes things very readable!</p><h2><a name="haskell-the-good-parts"></a>Haskell: the good parts</h2><p>With the disclaimers out of the way, I really just want to gush for a little bit. This is not going to be an objective, reasoned survey of why Haskell is good. I am not even really going to touch upon why types are so great and why purity is so wonderful—I’d love to discuss those in depth, but that’s for a different blog post. For now, I just want to touch upon the real surprises, the real things that made me <em>excited</em> about Haskell in ways I didn’t expect. These are the things that my subjective little experience has found fun.</p><h3><a name="language-extensions-are-haskell"></a>Language extensions <em>are</em> Haskell</h3><p>There was a time in my life when I spent a lot of time writing C. There are a lot of compilers for C, and they all implement the language in subtly different but often incompatible ways, especially on different platforms. The only way to maintain a modicum of predictability was to adhere to the standards <em>religiously</em>, even when certain GCC or MSVC extensions seem tantalizingly useful. I was actually bitten a few times by real instances where I figured I’d just use a harmless extension that was implemented everywhere, then found out it worked slightly differently across different compilers in a particular edge case. It was a learning experience.</p><p>It seems that this fear provides a very real distrust for using GHC’s numerous <em>language extensions</em>, and indeed, for a long time, I felt that it was probably an admirable goal to stick to Haskell 98 or Haskell 2010 as closely as possible. Sometimes I chose a slightly more verbose solution that was standard Haskell to avoid turning on a trivial extension that would make the code look a little bit cleaner.</p><p>About a year later, I’m finding that attitude was not only a mistake, but it forced me to often completely miss out on a lot of Haskell’s core value. GHC <em>won</em>, and now GHC and Haskell are basically synonymous. With that in mind, the portability concerns of language extensions are a bit of a non-issue, and turning them on is a very good idea! Some extensions are more than a little dangerous, so they cannot all be turned on without thinking, but the question is absolutely not “Is using language extensions a good idea?” and more “Is using <em>this</em> language extension a good idea?”</p><p>This is important, and I bring it up for a reason: so much of the awesomeness of Haskell is locked behind language extensions. Turning a lot of these on is one of the main things that made me really start to see how incredibly powerful Haskell actually is.</p><h3><a name="phantom-types"></a>Phantom types</h3><p>I’m going to start out by talking about <em>phantom types</em>, which are a pretty simple concept but a powerful one, and they serve as the foundation for a lot of other cool type-level tricks that can make Haskell extremely interesting. The basic idea of a phantom type is simple; it’s a type parameter that isn’t actually used to represent any particular runtime value:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type represents an id for some kind of value, but although the kind of value is specified in the type as the <code>a</code> type parameter, it isn’t actually used anywhere on the data definition—no matter what <code>a</code> is, an <code>Id</code> is just a piece of text. This makes it possible to write functions that operate on specific kinds of ids, and those invariants will be statically checked by the compiler, even though the runtime representation is entirely identical:</p><pre><code class="pygments"><span class="nf">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span></code></pre><p>Using <code>FlexibleInstances</code>, it’s also possible to create different instances for different kinds of ids. For example, it would be possible to have different <code>Show</code> instances depending on the type of id in question.</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"user #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"post #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span></code></pre><p>This provides a simple framework for encoding entirely arbitrary information into the type system, then asking the compiler to actually check assertions about that information. This is made even more powerful with some other extensions, which I’ll talk about shortly.</p><h3><a name="letting-the-compiler-write-code"></a>Letting the compiler write code</h3><p>One of the things I really dislike, more than most things, is boilerplate. A little bit of boilerplate is fine—even necessary at times—but as soon as I start wondering if a code generator would improve things, I think the programming language has pretty much failed me.</p><p>I write a lot of Racket because, in a sense, Racket is the ultimate boilerplate killer: the macro system is a first-class code generator integrated with the rest of the language, and it means that boilerplate is almost never an issue. Of course, that’s not always true: sometimes a bit of boilerplate <em>is</em> still necessary because macros cannot deduce enough information about the program to generate the code entirely on their own, and in Haskell, some of that information is actually present in the type system.</p><p>This leads to two absolutely incredible extensions, both of which are simple and related, but which actually <em>completely change</em> how I approach problems when programming. These two extensions are <code>GeneralizedNewtypeDeriving</code> and <code>StandaloneDeriving</code>.</p><h4><a name="newtypes-and-type-safety"></a>Newtypes and type safety</h4><p>The basic idea is that “newtypes” are just simple wrapper types in Haskell. This turns out to be extremely important when trying to find the value of Haskell because they allow you to harden type safety by specializing types to <em>your</em> domain. For example, consider a type representing a user’s name:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type is extremely simple, and in fact isn’t even at all different from a simple <code>Text</code> value with respect to its representation, since all combinations of unicode characters are allowed in a name. Therefore, what’s the point of a separate type? Well, this allows Haskell to introduce actual compilation failures when two different kinds of textual data are mixed. This is not a new idea, and even in languages that don’t support this sort of thing, Joel Spolsky’s old blog post <a href="http://www.joelonsoftware.com/articles/Wrong.html">Making Wrong Code Look Wrong</a> describes how it can be done by convention. Still, almost every modern language makes this possible: in C, it would be a single-member <code>struct</code>, in class-based OO languages, it would be a single-member class... this is not a complicated idea.</p><p>The difference lies in its usage. In other languages, this strategy is actually not very frequently employed for the simple reason that it is almost always extremely annoying. You are forced to do tons of wrapping/unwrapping, and at that point it isn’t really clear if you’re even getting all that much value out of the distinction when your first solution to a type mismatch is wrapping or unwrapping the value without a second thought. In Haskell, however, this can be heavily mitigated by asking the compiler to <em>automatically derive typeclass implementations</em>, which allow the unwrapping/wrapping to effectively happen implicitly for a constrained set of operations.</p><h4><a name="using-generalizednewtypederiving"></a>Using <code>GeneralizedNewtypeDeriving</code></h4><p>Consider the <code>Name</code> type once again, but this time, let’s derive a class:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">IsString</span><span class="p">)</span></code></pre><p>The <code>IsString</code> typeclass in Haskell allows custom types to automatically be created from string literals. It is <em>not</em> handled specially by Haskell’s <code>deriving</code> mechanism. Since <code>Text</code> implements <code>IsString</code>, an instance will be generated that simply defers to the underlying type, automatically generating the code to wrap the result up in a <code>Name</code> box at the end. This means that code like this will now just magically work:</p><pre><code class="pygments"><span class="nf">name</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Name</span> +<span class="nf">name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa P. Hacker"</span></code></pre><p>No boilerplate needs to be written! This is a neat trick, but it actually turns out to be far more useful than that simple example in practice. What really makes this functionality shine is when you want to derive <em>some</em> kinds of functionality but disallow some others. For example, using the <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a> package, it’s possible to do something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">ToText</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>This creates an opaque <code>Id</code> type, but it automatically generates conversions <em>to</em> textual formats. However, it does <em>not</em> automatically create <code>FromText</code> or <code>FromJSON</code> instances, which would be dangerous because decoding <code>Id</code>s can potentially fail. It’s then possible to write out those instances manually to preserve a type safety:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">FromText</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fromText</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="n">isValidId</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="kr">then</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">String</span><span class="w"> </span><span class="n">val</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">fromText</span><span class="w"> </span><span class="n">val</span><span class="p">)</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span></code></pre><h4><a name="using-standalonederiving"></a>Using <code>StandaloneDeriving</code></h4><p>The ordinary <code>deriving</code> mechanism is extremely useful, especially when paired with the above, but sometimes it is desirable to have a little bit more flexibility. In these cases, <code>StandaloneDeriving</code> can help.</p><p>Take the <code>Id</code> example again: it has a phantom type, and simply adding something like <code>deriving (ToText)</code> with derive <code>ToText</code> instances for <em>all</em> kinds of ids. It is potentially useful, however, to derive instances for more specific id types. Using standalone <code>deriving</code> constructs permits this sort of flexibility.</p><pre><code class="pygments"><span class="kr">deriving</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toText</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">postIdToText</span></code></pre><p>This is an example where GHC language extensions end up becoming significantly more than the sum of their parts, which seems to be a fairly frequent realization. The <code>StandaloneDeriving</code> mechanism is a little bit useful without <code>GeneralizedNewtypeDeriving</code>, but when combined, they are incredibly powerful tools for getting a very fine-grained kind of type safety <em>without</em> writing any boilerplate.</p><h3><a name="datakinds-are-super-cool-with-caveats"></a>DataKinds are super cool, with caveats</h3><p>Phantom types are quite wonderful, but they can only encode <em>types</em>, not arbitrary data. That’s where <code>DataKinds</code> and <code>KindSignatures</code> come in: they allow lifting arbitrary datatypes to the type level so that things that would normally be purely runtime values can be used at compile-time as well.</p><p>The way this works is pretty simple—when you define a datatype, you also define a “datakind”:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Registered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Anonymous</span></code></pre><p>Normally, the above declaration declares a <em>type</em>, <code>RegistrationStatus</code>, and two <em>data constructors</em>, <code>Registered</code> and <code>Anonymous</code>. With <code>DataKinds</code>, it also defines a <em>kind</em>, <code>RegistrationStatus</code>, and two <em>type constructors</em>, <code>Registered</code> and <code>Anonymous.</code></p><p>If that’s confusing, the way to understand that is to realize there is a sort of natural ordering here: types describe values, and kinds describe types. Therefore, turning on <code>DataKinds</code> “lifts” each definition by a single level, so types become kinds and values become types. This permits using these things at the type level:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>In this example, <code>UserId</code> still has a single phantom type variable, <code>s</code>, but this time it is constrained to the <code>RegistrationStatus</code> kind. Therefore, it can <em>only</em> be <code>Registered</code> or <code>Anonymous</code>. This cooperates well with the aforementioned <code>StandaloneDeriving</code> mechanism, and it mostly provides a convenient way to constrain type variables to custom kinds.</p><p>In general, <code>DataKinds</code> is a much more powerful extension, allowing things like type-level natural numbers or strings, which can be used to perform actual type-level computation (especially in combination with <code>TypeFamilies</code>) or a sort of metaprogramming. In some cases, they can even be used to implement things emulating things you can do with dependent types.</p><p>I think <code>DataKinds</code> are a very cool Haskell extension, but there are a couple caveats. One of the main ones is how new kinds are defined: <code>DataKinds</code> “hijacks” the existing datatype declaration syntax by making every single datatype declaration define a type <em>and</em> a kind. This is a little confusing, and it would be nice if a different syntax was used so that each could be defined independently.</p><p>Similarly, it seems that a lot of work is being done to allow using runtime values at the type level, but I wonder if people will ever need to use, say, runtime values at the <em>kind</em> level. This immediately evokes thoughts of Racket’s phase-based macro system, and I wonder if some of this duplication would be unnecessary with something similar.</p><p>Food for thought, but overall, <code>DataKinds</code> are a very nice addition to help with precisely and specifically typing particular problems.</p><h3><a name="typeclasses-can-emulate-effects"></a>Typeclasses can emulate effects</h3><p>This is something that I’ve found interesting in my time writing Haskell because I have <em>no idea</em> if it’s idiomatic or not, but it seems pretty powerful. The initial motivator for this idea was figuring out how to test our code without constantly dropping into <code>IO</code>.</p><p>More generally, we wanted to be able to unit test by “mocking” out collaborators, as it would be described in object oriented programming. I was always semi-distrustful of mocking, and indeed, it seems likely that it is heavily abused in certain circles, but I’ve come to appreciate the need that sometimes it is important to stub things out, <em>even in pure code</em>.</p><p>As an example, consider some code that needs access to the current time. This is something that would normally require <code>IO</code>, but we likely want to be able to use the value in a pure context without “infecting” the entire program with <code>IO</code> types. In Haskell, I have generally seen three ways of handling this sort of thing:</p><ol><li><p>Just inject the required values into the function and produce them “higher up” where I/O is okay. If threading the value around becomes too burdensome, use a Reader monad.</p></li><li><p>Use a free monad or similar to create a pure DSL of sorts, then write interpreters for various implementations, one of which uses <code>IO</code>.</p></li><li><p>Create custom monadic typeclasses that provide interfaces to the functionality you want to perform, then create instances, one of which is an instance over <code>IO</code>.</p></li></ol><p>This last approach seems to be less common in Haskell, but it’s the approach we took, and it seems to work out remarkably well. Returning to the need to get the current time, we could pretty easily write such a typeclass to encode that need:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">UTCTime</span></code></pre><p>Now we can write functions that use the current time:</p><pre><code class="pygments"><span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span><span class="p">)</span></code></pre><p>Now, we can write instances for <code>CurrentTime</code> that will allow us to run the same code in different contexts:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runAppM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">runTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">runTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftIO</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Time</span><span class="o">.</span><span class="kt">Clock</span><span class="o">.</span><span class="n">getCurrentTime</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">posixSecondsToUTCTime</span><span class="w"> </span><span class="mi">0</span></code></pre><p>Where this really starts to shine is when adding additional effects. For example, the above token validation function might also need information about some kind of secret used for signing. Under this model, it’s just another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getTokenSecret</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Secret</span> + +<span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">secret</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getTokenSecret</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span> +<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">verifySignature</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="n">secret</span><span class="p">)</span></code></pre><p>Of course, so far all of these functions have been extremely simple, and we’ve basically been using them as a glorified reader monad. In practice, though, we use this pattern for lots more than just retrieving values. For example, we might have a typeclass for database interactions:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> +<span class="w"> </span><span class="n">insertUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">PersistenceError</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">))</span></code></pre><p>With all of this done, it becomes incredibly easy to see which functions are using which effects:</p><pre><code class="pygments"><span class="nf">postUsers</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">postUsers</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">getHealthcheck</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">getHealthcheck</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>There’s no need to perform any lifting, and this all seems to scale quite nicely. We’ve written some additional utilities to help write tests against functions using these kinds of monadic interfaces, and even though there’s a little bit of annoying boilerplate in a few spots, overall it seems to work quite elegantly.</p><p>I’m not entirely sure how common this is in the Haskell community, but it’s certainly pretty neat how easy it is to get nearly all of the benefits of effect types in other languages simply by composing some of Haskell’s simplest features.</p><h3><a name="atom-s-ide-haskell-tooling-is-invaluable"></a>Atom’s ide-haskell tooling is invaluable</h3><p>Alright, so, confession time: I don’t use Emacs.</p><p>I know, I know, how is that possible? I write Lisp, after all. Well, honestly, I tried picking it up a number of times, but none of those times did I get far enough to ditch my other tools. For Racket work, I use DrRacket, but for almost everything else, I use Atom.</p><p>Atom has a lot of flaws, but it’s also pretty amazing in places, and I absolutely <em>love</em> the Haskell tooling written by the wonderful <a href="https://github.com/atom-haskell">atom-haskell</a> folks. I use it constantly, and even though it doesn’t always work perfectly, it works pretty well. When it has problems, I’ve at least figured out how to get it working correctly.</p><p>This is probably hard to really explain without seeing it for yourself, but I’ve found that I basically <em>depend</em> on this sort of tooling to be fully productive in Haskell, and I have no problem admitting that. The ability to get instant feedback about type errors tied to visual source locations, to be able to directly manipulate the source by selecting expressions and getting type information, and even the option to get inline linter suggestions means I spend a lot less time glancing at the terminal, and even less time in the REPL.</p><p>The tooling is far from perfect, and it leaves a lot to be desired in places (the idea of using that static information for automated, project-wide refactoring <em>a la</em> Java is tantalizing), but most of those things are ideas of what amazing things could be, not broken or missing essentials. I am pretty satisfied with ide-haskell right now, and I can only hope it continues to get better and better.</p><h2><a name="frustrations-drawbacks-and-pain-points"></a>Frustrations, drawbacks, and pain points</h2><p>Haskell is not perfect. In fact, far from it. There is a vast array of little annoyances that I have with the language, as is the case with any language. Still, there are a few overarching problems that I would really like to at least mention. These are the biggest sources of frustration for me so far.</p><h3><a name="purity-failure-and-exception-handling"></a>Purity, failure, and exception-handling</h3><p>One of Haskell’s defining features is its purity—I don’t think many would disagree with that. Some people consider it a drawback, others consider it one of its greatest boons. Personally, I like it a lot, and I think one of the best parts about it is how it requires the programmer to be incredibly deliberate about failure.</p><p>In many languages, when looking up a value from a container where the key doesn’t exist, there are really two ways to go about expressing this failure:</p><ol><li><p>Throw an exception.</p></li><li><p>Return <code>null</code>.</p></li></ol><p>The former is scary because it means <em>any</em> call to any function can make the entire program blow up, and it’s often impossible to know which functions even have the potential to throw. This creates a certain kind of non-local control flow that can sometimes cause a lot of unpredictability. The second option is much the same, especially when any value in a program might be <code>null</code>; it just defers the failure.</p><p>In languages with option types, this is somewhat mitigated. Java now has option types, too, but they are still frequently cumbersome to use because there is nothing like monads to use to simply chain operations together. Haskell, in comparison, has an incredible complement of tools to simply handle errors without a whole lot of burden on the programmer, and I have found that, in practice, this is <em>actually helpful</em> and I really do write better error-handling code.</p><h4><a name="first-the-good-parts"></a>First, the good parts</h4><p>I have seen a comparison drawn between throwing checked exceptions and returning <code>Maybe</code> or <code>Either</code> types, but in practice the difference is massive. Handling checked exceptions is a monotonous chore because they are not first-class values, they are actually entirely separate linguistic constructs. Consider a library that throws a <code>LibraryException</code>, and you want to wrap that library and convert those exceptions to <code>ApplicationException</code>s. Well, have fun writing this code dozens of times:</p><pre><code class="pygments"><span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomething</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span> + +<span class="c1">// ...</span> + +<span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomethingElse</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span></code></pre><p>In Haskell, failure is just represented by first-class values, and it’s totally possible to write helper functions to abstract over that kind of boilerplate:</p><pre><code class="pygments"><span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ApplicationError</span> +<span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">ApplicationError</span><span class="w"> </span><span class="n">a</span> +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapLeft</span><span class="w"> </span><span class="n">libraryToApplication</span></code></pre><p>Now, that same boilerplate-y code becomes nearly invisible:</p><pre><code class="pygments"><span class="nf">x</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomething</span> + +<span class="c1">-- ...</span> + +<span class="nf">y</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomethingElse</span></code></pre><p>This might not <em>seem</em> like much, but it really cuts down on the amount of visual noise, which ends up making all the difference. Boilerplate incurs a cost much bigger than simply taking the time to type it all out (though that’s important, too): the cognitive overhead of parsing which parts of a program are boilerplate has a significant impact on readability.</p><h4><a name="so-what-s-the-problem"></a>So what’s the problem?</h4><p>If error handling is so great in Haskell, then why am I putting it under the complaints section? Well, it turns out that not everyone seems to think it’s as great as I make it out to be because people seem to keep writing Haskell APIs that throw exceptions!</p><p>Despite what some purists would have you believe, Haskell has exceptions, and they are not uncommon. Lots of things can throw exceptions, some of which are probably reasonable. Failing to connect to a database is a pretty catastrophic error, so it seems fair that it would throw. On the other hand, inserting a duplicate record is pretty normal operation, so it seems like that should <em>not</em> throw.</p><p>I mostly treat exceptions in Haskell as unrecoverable catastrophes. If I throw an error in <em>my</em> code, I do not intend to catch it. That means something horrible happened, and I just want that horribleness to show up in a log somewhere so I can fix the problem. If I care about failure, there are better ways to handle that failure gracefully.</p><p>It’s also probably worth noting that exceptions in Haskell can be thrown from anywhere, even pure code, but can only be <em>caught</em> within the <code>IO</code> monad. This is especially scary, but I’ve seen it happen in actual libraries out in the wild, even ones that the entire Haskell ecosystem is built on. One of the crowning examples of this is the <code>text</code> package, which provides a function called <code>decodeUtf8</code> to convert bytestrings into text. Its type is very simple:</p><pre><code class="pygments"><span class="nf">decodeUtf8</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ByteString</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>But wait, what if the bytestring is not actually a valid UTF-8 string?</p><p>Boom. There goes the application.</p><p>Okay, okay, well, at least the <code>text</code> package provides another function, this one called <code>decodeUtf8'</code>, which returns an <code>Either</code>. This is good, and I’ve trained myself to only ever use <code>decodeUtf8'</code>, but it still has some pretty significant problems:</p><ul><li><p>The <em>safe</em> version of this function is the “prime” version, rather than the other way around, which encourages people to use the unsafe one. Ideally, the unsafe one should be explicitly labeled as such... maybe call it <code>unsafeDecodeUtf8</code>?</p></li><li><p>This is not a hypothetical problem. When using a Haskell JWT library, we found a function that converts a string into a JWT. Since not all strings are JWTs, the library intelligently returns a <code>Maybe</code>. Therefore, we figured we were safe.</p><p>A couple weeks later, we found that providing this function with invalid data was returning HTTP 500 errors. Why? Our error handling was meticulous! Well, the answer was a <code>decodeUtf8</code> call, hidden inside of the JWT library. This is especially egregious, given that the API it exposed returned a <code>Maybe</code> anyway! It would have been trivial to use the safe version there, instead, but the poor, misleading name led the library developer to overlook the bug lurking in the otherwise innocuous function.</p><p>Even worse, this function was totally pure, and we used it in pure code, so we could not simply wrap the function and catch the exception. We had two options: use <code>unsafePerformIO</code> (yuck!) or perform a check before handing the data to the buggy function. We chose the latter, but in some cases, I imagine that could be too difficult to do in order to make it feasible.</p></li></ul><p>The point I’m trying to make is that this is a real problem, and it seems to me that throwing exceptions invalidates one of the primary advantages of Haskell. It disappointed me to realize that a significant amount of code written by FP Complete, one of the primary authors of some of the most important “modern Haskell” code in existence (including Stack), seem to very frequently expose APIs that will throw.</p><p>I’m not sure how much of this stems from a fundamental divide in the Haskell ecosystem and how much it is simply due to Michael Snoyman’s coding style, given that he is the primary author of a number of these tools and libraries that seem very eager to throw exceptions. As just one example of a real situation in which we were surprised by this behavior, we used Snoyman’s http-client library and found that it mysteriously throws upon nearly <em>any</em> failure state:</p><blockquote><p>A note on exceptions: for the most part, all actions that perform I/O should be assumed to throw an <code>HttpException</code> in the event of some problem, and all pure functions will be total. For example, <code>withResponse</code>, <code>httpLbs</code>, and <code>BodyReader</code> can all throw exceptions.</p></blockquote><p>This doesn’t seem entirely unreasonable—after all, isn’t a failure to negotiate TLS fairly catastrophic?—until you consider our use case. We needed to make a subrequest during the extent of another HTTP request to our server, and if that subrequest fails, we absolutely need to handle that failure gracefully. Of course, this is not <em>terrible</em> given that we are in <code>IO</code> so we can actually catch these exceptions, but since this behavior was only noted in a single aside at the top of the documentation, we didn’t realize we were forgetting error handling until far too late and requests were silently failing.</p><p>Exceptions seem to devalue one of the most powerful concepts in Haskell: if I don’t consider all the possibilities, my code <em>does not compile</em>. In practice, when working with APIs that properly encode these possibilities into the type system, this value proposition seems to be real. I really do find myself writing code that works correctly as soon as it compiles. It’s almost magical.</p><p>Using exceptions throws that all out the window, and I wish the Haskell ecosystem was generally more cautious about when to use them.</p><h3><a name="the-string-problem"></a>The String problem</h3><p>I sort of alluded to this a tiny bit in the last section, and that is probably indicative of how bad this issue is. I’m just going to be blunt: <strong>In Haskell, strings suck.</strong></p><p>This is always a bit of an amusing point whenever it is discussed because of how silly it seems. Haskell is a research language with a cutting-edge type system and some of the fanciest features of any language in existence. When everyday programming might use things like “profunctors”, “injective type families”, and “generalized algebraic datatypes”, you would think that dealing with <em>strings</em> would be a well-solved problem.</p><p>But it isn’t. Haskell libraries frequently use not one, not two, but <em><strong>five</strong></em> kinds of strings. Let’s list them off, shall we?</p><ul><li><p>First off, there’s the built-in <code>String</code> type, which is actually an alias for the <code>[Char]</code> type. For those not intimately familiar with Haskell, that’s a <em>linked list of characters</em>. As <a href="http://www.stephendiehl.com/">Stephen Diehl</a> recently put it in <a href="http://www.stephendiehl.com/posts/strings.html">a blog post describing the disaster that is Haskell string types</a>:</p><blockquote><p>This is not only a bad representation, it’s quite possibly the least efficient (non-contrived) representation of text data possible and has horrible performance in both time and space. <em>And it’s used everywhere in Haskell.</em></p></blockquote><p>The point is, it’s really bad. This type is not a useful representation for textual data in practical applications.</p></li><li><p>Moving on, we have a fairly decent type, <code>Text</code>, which comes from <code>Data.Text</code> in the <code>text</code> package. This is a decent representation of text, and it’s probably the one that everything should use. Well, maybe. Because <code>Text</code> comes in two varieties: lazy and strict. Nobody seems to agree on which of those two should be used, though, and they are totally incompatible types: functions that work with one kind of text won’t work with the other. You have to manually convert between them.</p></li><li><p>Finally, we have <code>ByteString</code>, which is horribly misnamed because it really isn’t a string at all, at least not in the textual sense. A better name for this type would have simply been <code>Bytes</code>, which sounds a lot scarier. And that would be good, because data typed as a <code>ByteString</code> is as close as you can get in Haskell to not assigning a type at all: a bytestring holds arbitrary bytes without assigning them any meaning whatsoever!</p><p>Or at least, that’s the intention. The trouble is that people <em>don’t</em> treat bytestrings like that—they just use them to toss pieces of text around, even when those pieces of text have a well-defined encoding and represent textual data. This leads to the <code>decodeUtf8</code> problem mentioned above, but it’s bigger than that because it often ends up with some poor APIs that assign some interpretation to <code>ByteString</code> data without assigning it a different type.</p><p>Again, this is throwing away so much of Haskell’s safety. It would be like using <code>Int</code> to keep track of boolean data (“just use 0 and 1!”) or using empty and singleton lists instead of using <code>Maybe</code>. When you use the precise type, you encode invariants and contracts into statically-checked assertions, but when you use general types like <code>ByteString</code>, you give that up.</p><p>Oh, and did I mention that <code>ByteString</code>s also come in incompatible lazy and strict versions, too?</p></li></ul><p>So, obviously, the answer is to just stop using the bad types and to just use (one kind of) <code>Text</code> everywhere. Great! Except that the other types are totally inescapable. The entire standard library uses <code>String</code> exclusively—after all, <code>text</code> is a separate package—and small libraries often use <code>String</code> instead of <code>text</code> because they have no need to bring in the dependency. Of course, this just means every real application pays the performance hit of converting between all these different kinds of strings.</p><p>Similarly, those that <em>do</em> use <code>Text</code> often use different kinds of text, so code ends up littered with <code>fromStrict</code> or <code>toStrict</code> coercions, which (again) have a cost. I’ve already ranted enough about <code>ByteString</code>, but basically, if you’re using <code>ByteString</code> in your API to pass around data that is semantically text, you are causing me pain. Please stop.</p><p>It seems that the way <code>Data.Text</code> probably <em>should</em> have been designed was by making <code>Text</code> a typeclass, then making the lazy and strict implementations instances of that typeclass. Still, the fact that both of them exist would always cause problems. I’m actually unsure which one is the “correct” choice—I don’t know enough about how the two perform in practice—but it seems likely that picking <em>either</em> one would be a performance improvement over the current system, which is constantly spending time converting between the two.</p><p>This issue has been ranted about plenty, so I won’t ramble on, but if you’re designing new libraries, please, <em>please</em> use <code>Text</code>. Your users will thank you.</p><h3><a name="documentation-is-nearly-worthless"></a>Documentation is nearly worthless</h3><p>Finally, let’s talk about documentation.</p><p>One of my favorite programming languages is Racket. Racket has a documentation tool called Scribble. Scribble is special because it is a totally separate domain-specific language for writing documentation, and it makes it fun and easy to write good explanations. There are even forms for typesetting automatically-rendered examples that look like a REPL. If the examples ever break or become incorrect, the docs don’t even compile.</p><p>All of the Racket core library documentation makes sure to set a good example about what good documentation should look like. The vast majority of the documentation is paragraphs of prose and simple but practical examples. There are also type signatures (in the form of contracts), and those are super important, but they are so effective because of how the prose explains what each function does, when to use it, <em>why</em> you’d use it, and <em>why you wouldn’t use it</em>.</p><p>Everything is cross-referenced automatically. The documentation is completely searchable locally out of the box. As soon as you install a package, its docs are automatically indexed. User-written libraries tend to have pretty good docs, too, because the standard libraries set such a good example <em>and</em> because the tools are so fantastic. Racket docs are really nice, and they’re so good they actually make things like Stack Overflow or even Google mostly irrelevant. It’s all there in the manual.</p><p>Haskell documentation is the opposite of everything I just said.</p><ul><li><p>The core libraries are poorly documented. Most functions include a sentence of description, and almost none include examples. At their worst, the descriptions simply restate the type signature.</p></li><li><p>Third-party libraries’ documentation is even worse, going frequently completely undocumented and actually only including type signatures and nothing else.</p></li><li><p>Haddock is an incredibly user-hostile tool for writing anything other than tiny snippets of documentation and is not very good at supporting prose. Notably, Haddock’s documentation is not generated using Haddock (and it still manages to be almost unusable). Forcing all documentation into inline comments makes users unlikely to write much explanation, and there is no ability for abstraction.</p></li><li><p>Reading documentation locally is very difficult because there is no easy way to open documentation for a particular package in a web browser, and it’s <em>certainly</em> not searchable. This is especially ridiculous given that Hoogle exists, which is one of best ways to search API docs in existence. There should be a <code>stack hoogle</code> command that just opens a Hoogle page for all locally-installed packages and Just Works, but there isn’t.</p></li><li><p>Most valuable information exists outside of documentation, so Google becomes a go-to immediately after a quick glance at the docs, and information is spread across blog posts, mailing lists, and obscure reddit posts.</p></li></ul><p>This is a problem that cannot be fixed by just making Haddock better, nor can it be fixed simply by improving the existing standard library documentation. There is a fundamental problem with Haskell documentation (which, to be completely fair, is not unique to Haskell), which is that its tools do not support anything more than API docs.</p><p>Good documentation is so much more than “here’s what this function does”; it’s about guides and tutorials and case studies and common pitfalls. <a href="http://docs.racket-lang.org/lens/lens-guide.html">This is documentation for someone new to lenses.</a> <a href="https://hackage.haskell.org/package/lens#readme">This is not.</a> Take note of the difference.</p><h2><a name="conclusion-and-other-thoughts"></a>Conclusion and other thoughts</h2><p>Haskell is an incredible programming platform, and indeed, it is sometimes mind-boggling how complete it is. It also has a lot of rough edges, sometimes in places that feel like they need a lot more care, or perhaps they’re even simply unfinished.</p><p>I could spend weeks writing about all the things I really like or dislike about the language, discussing in fine detail all the things that have made me excited or all the little bits that have made me want to tear my hair out. Heck, I could probably spend a month writing about strings alone. That’s not the point, though... I took a risk with Haskell, and it’s paid off. I’m not yet sure exactly how I feel about it, or when I would chose it relative to other tools, but it is currently very high on my list of favorite technologies.</p><p>I did not come to Haskell with a distaste for static typing, despite the fact that I write so much Racket, a dynamically typed language (by default, at least). I don’t really use Typed Racket, and despite my love for Haskell and its type system, I am not sure I will use much more of it than I did before. Haskell and Racket are very different languages, which is justified in some places and probably sort of circumstantial in others.</p><p>The future of Haskell seems bright, and a lot of the changes in the just-released GHC 8 are extremely exciting. I did not list records as a pain point because the changes in GHC 8 appear to make them a <em>lot</em> more palatable, although whether or not they solve that problem completely remains to be seen. I will absolutely continue to write Haskell and push it to its limits where I can, and hopefully try and take as much as I can from it along the way.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/haskell.rss.xml b/feeds/haskell.rss.xml new file mode 100644 index 0000000..1dc3cf9 --- /dev/null +++ b/feeds/haskell.rss.xml @@ -0,0 +1,1510 @@ +Posts tagged ‘haskell’ | Alexis King’s BlogPosts tagged ‘haskell’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/haskell.html25 Mar 202125 Mar 202160An introduction to typeclass metaprogramminghttps://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/https://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/25 Mar 2021<article><p><em>Typeclass metaprogramming</em> is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem), and it is the core mechanism used to implement generic programming via <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">GHC generics</a>. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.</p><p>This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does <em>not</em> attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also <em>not</em> a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.</p><h2><a name="part-1-basic-building-blocks"></a>Part 1: Basic building blocks</h2><p>Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.</p><p>To start, we’ll cover the absolute foundations of typeclass metaprogramming.</p><h3><a name="typeclasses-as-functions-from-types-to-terms"></a>Typeclasses as functions from types to terms</h3><p>As its name implies, typeclass metaprogramming (henceforth TMP<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup>) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic <code>==</code> operator via the <code>Eq</code> class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: <strong>typeclasses are functions from types to (runtime) terms</strong>.</p><p>What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called <code>TypeOf</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Given these instances, we can observe that they do what we expect in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>Note that both the <code>TypeOf Bool</code> and <code>TypeOf Char</code> instances ignore the argument to <code>typeOf</code> altogether. This makes sense, as the whole point of the <code>TypeOf</code> class is to get access to <em>type</em> information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This typeclass definition is a little unusual, as the type parameter <code>a</code> doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>the full type of the <code>show</code> method is implicitly extended with a <code>Show a</code> constraint to yield:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Furthermore, if we write <code>forall</code>s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the <em>full</em> type of <code>show</code>:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>In the same vein, we can write out the full type of <code>typeOf</code>, as given by our new definition of <code>TypeOf</code>:</p><pre><code class="pygments"><span class="nf">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This type is still unusual, as the <code>a</code> type parameter doesn’t appear anywhere to the right of the <code>=&gt;</code> arrow. This makes the type parameter trivially <em>ambiguous</em>, which is to say it’s impossible for GHC to infer what <code>a</code> should be at any call site. Fortunately, <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html">we can use <code>TypeApplications</code></a> to pass a type for <code>a</code> directly, as we can see in the updated definition of <code>TypeOf (a, b)</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Once again, we can test out our new definitions in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="kt">Bool</span> +<span class="s">"Bool"</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Bool</span><span class="p">,</span><span class="w"> </span><span class="kt">Char</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our <code>typeOf</code> function is, quite literally, a function that accepts a single type as an argument and returns a term-level <code>String</code>. Of course, the <code>TypeOf</code> typeclass is not a particularly <em>useful</em> example of such a function, but it demonstrates how easy it is to construct.</p><h3><a name="type-level-interpreters"></a>Type-level interpreters</h3><p>One important consequence of eliminating the value-level argument of <code>typeOf</code> is that there is no need for its argument type to actually be <em>inhabited</em>. For example, consider the <code>TypeOf</code> instance on <code>Void</code> from <code>Data.Void</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Void</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Void"</span></code></pre><p>This above instance is no different from the ones on <code>Bool</code> and <code>Char</code> even though <code>Void</code> is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of <code>TypeOf</code> on lists:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"["</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"]"</span></code></pre><p>If <code>typeOf</code> required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type <code>a</code> to recursively apply <code>typeOf</code> to. But since <code>typeOf</code> only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.</p><p>A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Z</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="n">a</span></code></pre><p>It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:</p><ul><li><p><code>Z</code> is a type that represents 0.</p></li><li><p><code>S Z</code> is a type that represents 1.</p></li><li><p><code>S (S Z)</code> is a type that represents 2.</p></li></ul><p>And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Numeric.Natural</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>As its name implies, <code>reifyNat</code> reifies a type-level natural number encoded using our datatypes above into a term-level <code>Natural</code> value:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="kt">Z</span> +<span class="mi">0</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span> +<span class="mi">1</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="mi">2</span></code></pre><p>One way to think about <code>reifyNat</code> is as an <em>interpreter</em> of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.</p><h3><a name="overlapping-instances"></a>Overlapping instances</h3><p>Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for <code>Show (Maybe a)</code>, you aren’t supposed to <em>also</em> write an instance for <code>Show (Maybe Bool)</code>, since it isn’t clear whether <code>show (Just True)</code> should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.</p><p>Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.</p><p>As a simple example, suppose we wanted to write a typeclass that checks whether a given type is <code>()</code> or not:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:</p><pre><code class="pygments"><span class="c1">-- not actually valid Haskell, just an example</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>But if we try to translate this to typeclass instances, we’ll get a problem:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> This means GHC will complain about instance overlap if we try to evaluate <code>isUnit @()</code>:</p><pre><code>ghci&gt; isUnit @() + +error: + • Overlapping instances for IsUnit () + arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance IsUnit () +</code></pre><p>To fix this, we have to explicitly mark <code>IsUnit ()</code> as overlapping:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>Now GHC accepts the expression without complaint:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="nb">()</span> +<span class="kt">True</span></code></pre><p>What does the <code>{-# OVERLAPPING #-}</code> pragma do, exactly? The gory details are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances">spelled out in the GHC User’s Guide</a>, but the simple explanation is that <code>{-# OVERLAPPING #-}</code> relaxes the overlap checker as long as the instance is <em>strictly more specific</em> than the instance(s) it overlaps with. In this case, that is true: <code>IsUnit ()</code> is trivially more specific than <code>IsUnit a</code>, since the former only matches <code>()</code> while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.</p><p>Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>The intent of <code>guardUnit</code> is to use <code>isUnit</code> to detect if its argument is of type <code>()</code>, and if it is, to return an error. However, even though we marked <code>IsUnit ()</code> overlapping, we still get an overlapping instance error:</p><pre><code>error: + • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance [overlapping] IsUnit () + • In the expression: isUnit @a +</code></pre><p>What gives? The problem is that GHC simply doesn’t know what type <code>a</code> is when compiling <code>guardUnit</code>. It <em>could</em> be instantiated to <code>()</code> where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.</p><p>This behavior is actually a very, very good thing. If GHC were to blindly pick the <code>IsUnit a</code> instance in this case, then <code>guardUnit</code> would always take the <code>False</code> branch, even when passed a value of type <code>()</code>! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when <code>{-# OVERLAPPING #-}</code> annotations are used, so it’s important to keep their limitations in mind.</p><p>As it happens, in this particular case, the error is easily remedied. We simply have to add an <code>IsUnit</code> constraint to the type signature of <code>guardUnit</code>:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now picking the right <code>IsUnit</code> instance is deferred to the place where <code>guardUnit</code> is used, and the definition is accepted.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><h3><a name="type-families-are-functions-from-types-to-types"></a>Type families are functions from types to types</h3><p>In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE TypeFamilies #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>The above is a <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families">closed type family</a>, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of <code>Sum</code> would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="nf">sum</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span> +<span class="nf">sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="nf">sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="n">sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.</p><p>To test our definition of <code>Sum</code> in GHCi, we can use <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind">the <code>:kind!</code> command</a>, which prints out a type and its kind after reducing it as much as possible:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span></code></pre><p>We can also combine <code>Sum</code> with our <code>ReifyNat</code> class from earlier:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.</p><h3><a name="example-1-generalized-concat"></a>Example 1: Generalized <code>concat</code></h3><p>Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a <code>flatten</code> function similar to like-named functions provided by many dynamically-typed languages. In those languages, <code>flatten</code> is like <code>concat</code>, but it works on a list of arbitrary depth. For example, we might use it like this:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]</span></code></pre><p>In Haskell, lists of different depths have different types, so multiple levels of <code>concat</code> have to be applied explicitly. But using TMP, we can write a generic <code>flatten</code> function that operates on lists of any depth!</p><p>Since this is <em>typeclass</em> metaprogramming, we’ll unsurprisingly begin with a typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="o">???</span><span class="p">]</span></code></pre><p>Our first challenge is writing the return type of <code>flatten</code>. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>Now we can write our <code>Flatten</code> instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>The inductive case is when the type is a nested list, in which case we want to apply <code>concat</code> and recur:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">(</span><span class="n">concat</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Sadly, if we try to compile these definitions, GHC will reject our <code>Flatten [a]</code> instance:</p><pre><code>error: + • Couldn't match type ‘a’ with ‘ElementOf [a]’ + ‘a’ is a rigid type variable bound by + the instance declaration + Expected type: [ElementOf [a]] + Actual type: [a] + • In the expression: x + In an equation for ‘flatten’: flatten x = x + In the instance declaration for ‘Flatten [a]’ + | + | flatten x = x + | ^ +</code></pre><p>At first blush, this error looks very confusing. Why doesn’t GHC think <code>a</code> and <code>ElementOf [a]</code> are the same type? Well, consider what would happen if we picked a type like <code>[Int]</code> for <code>a</code>. Then <code>[a]</code> would be <code>[[Int]]</code>, a nested list, so the first case of <code>ElementOf</code> would apply. Therefore, GHC refuses to pick the second equation of <code>ElementOf</code> so hastily.</p><p>In this particular case, we might think that’s rather silly. After all, if <code>a</code> were <code>[Int]</code>, then GHC wouldn’t have picked the <code>Flatten [a]</code> instance to begin with, it would pick the more specific <code>Flatten [[a]]</code> instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.</p><p>Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our <code>Flatten [a]</code> instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>This is a <em>type equality constraint</em>. Type equality constraints are written with the syntax <code>a ~ b</code>, and they state that <code>a</code> must be the same type as <code>b</code>. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that <code>ElementOf [a]</code> must always be <code>a</code>, which allows the instance to typecheck.</p><p>Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must <em>eventually</em> be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the <code>Flatten [a]</code> instance is selected, GHC will know that <code>a</code> is <em>not</em> a list type, and it will be able to reduce <code>ElementOf [a]</code> to <code>a</code> without difficulty. Indeed, we can see this for ourselves by using <code>flatten</code> in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span></code></pre><p>It works! But why do we need the type annotation on <code>1</code>? If we leave it out, we get a rather hairy type error:</p><pre><code>error: + • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ + Expected type: [ElementOf [a]] + Actual type: [ElementOf [a0]] + NB: ‘ElementOf’ is a non-injective type family + The type variable ‘a0’ is ambiguous +</code></pre><p>The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a <code>Num [a]</code> instance, in which case <code>1</code> could actually have a list type, and either case of <code>ElementOf</code> could match depending on the choice of <code>Num</code> instance. Of course, no such <code>Num</code> instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.</p><p>This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call <code>flatten</code> on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.</p><p>That wrinkle aside, <code>flatten</code> is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.</p><h4><a name="typeclasses-as-compile-time-code-generation"></a>Typeclasses as compile-time code generation</h4><p>Presented with the above definition of <code>Flatten</code>, it might not be immediately obvious how to think about <code>Flatten</code> as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, <code>Eq</code> or <code>Show</code>) than the <code>TypeOf</code> and <code>ReifyNat</code> classes we defined above.</p><p>One useful way to shift our perspective is to consider equivalent <code>Flatten</code> instances written using point-free style:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">id</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">concat</span></code></pre><p>These definitions of <code>flatten</code> no longer (syntactically) depend on term-level arguments, just like our definitions of <code>typeOf</code> and <code>reifyNat</code> didn’t accept any term-level arguments above. This allows us to consider what <code>flatten</code> might “expand to” given a type argument alone:</p><ul><li><p><code>flatten @[Int]</code> is just <code>id</code>, since the <code>Flatten [a]</code> instance is selected.</p></li><li><p><code>flatten @[[Int]]</code> is <code>flatten @[Int] . concat</code>, since the <code>Flatten [[a]]</code> instance is selected. That then becomes <code>id . concat</code>, which can be further simplified to just <code>concat</code>.</p></li><li><p><code>flatten @[[[Int]]]</code> is <code>flatten @[[Int]] . concat</code>, which simplifies to <code>concat . concat</code> by the same reasoning above.</p></li><li><p><code>flatten @[[[[Int]]]]</code> is then <code>concat . concat . concat</code>, and so on.</p></li></ul><p>This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of <code>flatten</code> takes a type as an argument and produces some number of composed <code>concat</code>s as a result. From this perspective, <code>Flatten</code> is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.</p><p>This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name <em>metaprogramming</em>. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.</p><h2><a name="part-2-generic-programming"></a>Part 2: Generic programming</h2><p>Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.</p><p>In the previous section, we discussed how to use TMP to write a generic <code>flatten</code> operation. In this section, we’ll aim a bit higher: totally generic functions that operate on <em>arbitrary</em> datatypes.</p><h3><a name="open-type-families-and-associated-types"></a>Open type families and associated types</h3><p>Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, <em>open type families</em>. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using <code>type instance</code> declarations. For example, we could define our <code>Sum</code> family from above like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>In the case of <code>Sum</code>, this would not be very useful, and indeed, <code>Sum</code> is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.</p><p>This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a <code>Key</code> open type family that relates types to the types used to index them:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span></code></pre><p>This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>In this case, anyone could define their own data structure, define instances of <code>Key</code> and <code>HasKey</code> for their data structure, and use <code>hasKey</code> to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>An open family declared inside a typeclass like this is called an <em>associated type</em>. It works exactly the same way as the separate definitions of <code>Key</code> and <code>HasKey</code>, it just uses a different syntax. Note that although the <code>family</code> and <code>instance</code> keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).</p><p>Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like <a href="https://hackage.haskell.org/package/mono-traversable"><code>mono-traversable</code></a> are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.</p><p>However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes <em>heavy</em> use of open type families: datatype-generic programming.</p><h3><a name="example-2-datatype-generic-programming"></a>Example 2: Datatype-generic programming</h3><p><em>Datatype-generic programming</em> refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include</p><ul><li><p>equality, comparison, and hashing,</p></li><li><p>recursive traversal of self-similar data structures, and</p></li><li><p>serialization and deserialization,</p></li></ul><p>among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on <em>any</em> datatype.</p><p>In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.</p><h4><a name="generic-datatype-representations"></a>Generic datatype representations</h4><p>At the heart of the <code>Generic</code> class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Authentication</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AuthBasic</span><span class="w"> </span><span class="kt">Username</span><span class="w"> </span><span class="kt">Password</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AuthSSH</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>then we have a type that is essentially equivalent to this one:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>If we know how to define a function on a nested tree built out of <code>Either</code>s and pairs, then we know how to define it on <em>any</em> such datatype! This is where TMP comes in: recall the way we viewed <code>Flatten</code> as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?</p><p>The answer to that question is <em>yes</em>. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, <code>numFields (AuthBasic "alyssa" "pass1234")</code> would return <code>2</code>, while <code>numFields (AuthSSH "&lt;key&gt;")</code> would return <code>1</code>. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.</p><p>We’ll start by using TMP to implement a “generic” version of <code>numFields</code> that operates on trees of <code>Either</code>s and pairs as described above:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="c1">-- base case: leaf value</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>Just like our <code>Flatten</code> class from earlier, <code>GNumFields</code> uses the type-level structure of its argument to choose what to do:</p><ul><li><p>If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.</p></li><li><p>If we find <code>Left</code> or <code>Right</code>, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.</p></li><li><p>In the case of any other value, we’re at a “leaf” in the tree of <code>Either</code>s and pairs, which corresponds to a single field, so we just return <code>1</code>.</p></li></ul><p>Now if we call <code>gnumFields (Left ("alyssa", "pass1234"))</code>, we’ll get <code>2</code>, and if we call <code>gnumFields (Right "&lt;key&gt;")</code>, we’ll get <code>1</code>. All that’s left to do is write a bit of code that converts our <code>Authentication</code> type to a tree of <code>Either</code>s and pairs:</p><pre><code class="pygments"><span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericizeAuthentication</span></code></pre><p>Now we get the results we want on our <code>Authentication</code> type using <code>numFieldsAuthentication</code>, but we’re not done yet, since it only works on <code>Authentication</code> values. Is there a way to define a generic <code>numFields</code> function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFields</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericize</span></code></pre><p>Now <code>numFields (AuthBasic "alyssa" "pass1234")</code> returns <code>2</code>, as desired, and it will <em>also</em> work with any datatype that provides a <code>Generic</code> instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:</p><ul><li><p>First, we define the <code>Generic</code> class, comprised of two parts:</p><ol><li><p>The <code>Rep a</code> associated type maps a type <code>a</code> onto its generic, sums-of-products representation, i.e. one built out of combinations of <code>Either</code> and pairs.</p></li><li><p>The <code>genericize</code> method converts an actual <em>value</em> of type <code>a</code> to the equivalent value using the sums-of-products representation.</p></li></ol></li><li><p>Next, we define a <code>Generic</code> instance for <code>Authentication</code>. <code>Rep Authentication</code> is the sums-of-products representation we described above, and <code>genericize</code> is likewise <code>genericizeAuthentication</code> from above.</p></li><li><p>Finally, we define <code>numFields</code> as a function with a <code>GNumFields (Rep a)</code> constraint. This is where all the magic happens:</p><ul><li><p>When we apply <code>numFields</code> to a datatype, <code>Rep</code> retrieves its generic, sums-of-products representation type.</p></li><li><p>The <code>GNumFields</code> class then uses various TMP techniques we’ve already described so far in this blog post to generate a <code>numFields</code> implementation on the fly from the structure of <code>Rep a</code>.</p></li><li><p>Finally, that generated <code>numFields</code> implementation is applied to the genericized term-level value, and the result is produced.</p></li></ul></li></ul><p>After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) <code>numFields</code> operation. Surely just defining <code>numFields</code> on each type directly would be far easier? Indeed, if we were just considering <code>numFields</code>, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined <code>numFields</code>, and all of them would automatically work on <code>Authentication</code> because they all leverage the same <code>Generic</code> instance!</p><p>This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation <em>once</em>, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.</p><h4><a name="improving-our-definition-of-generic"></a>Improving our definition of <code>Generic</code></h4><p>You may note that the definition of <code>Generic</code> provided above does not match the one in <code>GHC.Generic</code>. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a <code>GHC.Generics</code> tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.</p><h5><a name="distinguishing-leaves-from-the-spine"></a>Distinguishing leaves from the spine</h5><p>One problem with our version of <code>Generic</code> is that it provides no way to distinguish an <code>Either</code> or pair that should be considered a “leaf”, as in a type like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">A</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">B</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">)</span></code></pre><p>Given this type, <code>Rep Foo</code> should be <code>Either (Either Int String) (Char, Bool)</code>, and <code>numFields (Right ('a', True))</code> will erroneously return <code>2</code> rather than <code>1</code>. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">getLeaf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Now our <code>Generic</code> instances look like this:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">PublicKey</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">key</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">))</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">A</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">B</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Since the <code>Leaf</code> constructor now distinguishes a leaf, rather than the absence of an <code>Either</code> or <code>(,)</code> constructor, we’ll have to update our <code>GNumFields</code> instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.</p><h5><a name="handling-empty-constructors"></a>Handling empty constructors</h5><p>Suppose we have a type with nullary data constructors, like the standard <code>Bool</code> type:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>How do we write a <code>Generic</code> instance for <code>Bool</code>? Using just <code>Either</code>, <code>(,)</code>, and <code>Leaf</code>, we can’t, but if we are willing to add a case for <code>()</code>, we can use it to denote nullary constructors:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="nb">()</span></code></pre><p>In a similar vein, we could use <code>Void</code> to represent datatypes that don’t have any constructors at all.</p><h4><a name="continuing-from-here"></a>Continuing from here</h4><p>The full version of <code>Generic</code> has a variety of further improvements useful for generic programming, including:</p><ul><li><p>Support for converting from <code>Rep a</code> to <code>a</code>.</p></li><li><p>Special indication of self-recursive datatypes, making generic tree traversals possible.</p></li><li><p>Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.</p></li><li><p>Fully automatic generation of <code>Generic</code> instances via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric">the <code>DeriveGeneric</code> extension</a>, which reduces the per-type boilerplate to essentially nothing.</p></li></ul><p>The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.</p><h2><a name="part-3-dependent-typing"></a>Part 3: Dependent typing</h2><p>It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.</p><h3><a name="datatype-promotion"></a>Datatype promotion</h3><p>In part 1, we used uninhabited datatypes like <code>Z</code> and <code>S a</code> to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, <code>True</code> and <code>False</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">True</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">False</span></code></pre><p>Now we could define type families to provide operations on these types, such as <code>Not</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>However, this has some frustrating downsides:</p><ul><li><p>First, it’s simply inconvenient that we have to define these new <code>True</code> and <code>False</code> “dummy” types, which are completely distinct from the <code>Bool</code> type provided by the prelude.</p></li><li><p>More significantly, it means <code>Not</code> has a very unhelpful kind:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span></code></pre><p>Even though <code>Not</code> is only <em>supposed</em> to be applied to <code>True</code> or <code>False</code>, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like <code>Not Char</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span> +<span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span></code></pre><p>Rather than getting an error, GHC simply spits <code>Not Char</code> back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.</p></li></ul><p>One way to think about <code>Not</code> is that it is largely <em>dynamically kinded</em> in the same way some languages are dynamically typed. That isn’t entirely true, as we technically <em>will</em> get a kind error if we try to apply <code>Not</code> to a type constructor rather than a type, such as <code>Maybe</code>:</p><pre><code>ghci&gt; :kind! Not Maybe + +&lt;interactive&gt;:1:5: error: + • Expecting one more argument to ‘Maybe’ + Expected a type, but ‘Maybe’ has kind ‘* -&gt; *’ +</code></pre><p>…but <code>*</code> is still a very big kind, much bigger than we would like to permit for <code>Not</code>.</p><p>To help with both these problems, GHC provides <em>datatype promotion</em> via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html">the <code>DataKinds</code> language extension</a>. The idea is that for each normal, non-GADT type definition like</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>then in addition to the normal type constructor and value constructors, GHC also defines several <em>promoted</em> constructors:</p><ul><li><p><code>Bool</code> is allowed as both a type and a kind.</p></li><li><p><code>'True</code> and <code>'False</code> are defined as new types of kind <code>Bool</code>.</p></li></ul><p>We can see this in action if we remove our <code>data True</code> and <code>data False</code> declarations and adjust our definition of <code>Not</code> to use promoted constructors:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DataKinds #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;True</span></code></pre><p>Now the inferred kind of <code>Not</code> is no longer <code>* -&gt; *</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>Consequently, we will now get a kind error if we attempt to apply <code>Not</code> to anything other than <code>'True</code> or <code>'False</code>:</p><pre><code>ghci&gt; :kind! Not Char + +&lt;interactive&gt;:1:5: error: + • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ +</code></pre><p>This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">&#39;Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">&#39;S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>Note that we need to add an explicit kind signature on the definition of the <code>ReifyNat</code> typeclass, since otherwise GHC will assume <code>a</code> has kind <code>*</code>, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that <code>Z</code> and <code>S</code> are related, this prevents someone from coming along and defining a nonsensical instance like <code>ReifyNat Char</code>, which previously would have been allowed but will now be rejected with a kind error.</p><p>Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.</p><h3><a name="gadts-and-proof-terms"></a>GADTs and proof terms</h3><p>So far in this blog post, we have discussed several different function-like things:</p><ul><li><p>Ordinary Haskell functions are functions from terms to terms.</p></li><li><p>Type families are functions from types to types.</p></li><li><p>Typeclasses are functions from types to terms.</p></li></ul><p>A curious reader may wonder about the existence of a fourth class of function:</p><ul><li><p><em>???</em> are functions from terms to types.</p></li></ul><p>To reason about what could go in the <em>???</em> above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?</p><p>In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.</p><p>GADTs<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup> are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html">described in detail in the GHC User’s Guide</a>, but the key idea for our purposes is that <em>pattern-matching on a GADT constructor can refine type information</em>. Here’s a simple, silly example:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Bool</span> +<span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Int</span> + +<span class="nf">doSomething</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">x</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span></code></pre><p>Here, <code>WhatIsIt</code> is a datatype with two nullary constructors, <code>ABool</code> and <code>AnInt</code>, similar to a normal, non-GADT datatype like this one:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AnInt</span></code></pre><p>What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, <code>ABool</code> and <code>AnInt</code> would both have the type <code>forall a. WhatIsIt a</code>, but in the GADT definition, we explicitly fix <code>a</code> to <code>Bool</code> in the type of <code>ABool</code> and to <code>Int</code> in the type of <code>AnInt</code>.</p><p>This simple feature allows us to do very interesting things. The <code>doSomething</code> function is polymorphic in <code>a</code>, but on the right-hand side of the first equation, <code>x</code> has type <code>Bool</code>, while on the right-hand side of the second equation, <code>x</code> has type <code>Int</code>. This is because the <code>WhatIsIt a</code> argument effectively constrains the type of <code>a</code>, as we can see by experimenting with <code>doSomething</code> in GHCi:</p><pre><code>ghci&gt; doSomething ABool True +False +ghci&gt; doSomething AnInt 10 +11 +ghci&gt; doSomething AnInt True + +error: + • Couldn't match expected type ‘Int’ with actual type ‘Bool’ + • In the second argument of ‘doSomething’, namely ‘True’ + In the expression: doSomething AnInt True + In an equation for ‘it’: it = doSomething AnInt True +</code></pre><p>One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The <code>ABool</code> constructor is a proof of <code>a ~ Bool</code>, while the <code>AnInt</code> constructor is a proof of <code>a ~ Int</code>. When you construct <code>ABool</code> or <code>AnInt</code>, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.</p><p>GADTs can be much more sophisticated than our simple <code>WhatIsIt</code> type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:</p><pre><code class="pygments"><span class="kr">infixr</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span> + +<span class="kr">data</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">HCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This type is a <em>heterogenous list</em>, a list that can contain elements of different types:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">t</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Num</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[Bool, [Char]</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>An <code>HList</code> is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total <code>head</code> function on <code>HList</code>s like this:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Remarkably, GHC does not complain that this definition of <code>head</code> is non-exhaustive. Since we specified that the argument must be of type <code>HList (a ': as)</code> in the type signature for <code>head</code>, GHC knows that the argument <em>cannot</em> be <code>HNil</code> (which would have the type <code>HList '[]</code>), so it doesn’t ask us to handle that case.</p><p>These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.</p><h4><a name="proofs-that-work-together"></a>Proofs that work together</h4><p>This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an <code>HList</code> of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with <code>head</code>, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.</p><p>However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a <em>proof term</em> that contains no values, it just encapsulates type equalities on a type-level list:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a]</span> +<span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b]</span> +<span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b, c]</span></code></pre><p>We call it a proof term because a value of type <code>OneToThree a b c as</code> constitutes a <em>proof</em> that <code>as</code> has exactly 1, 2, or 3 elements. Using <code>OneToThree</code>, we can write a function that accepts an <code>HList</code> accompanied by a proof term:</p><pre><code class="pygments"><span class="nf">sumUpToThree</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">z</span></code></pre><p>As with <code>head</code>, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:</p><ul><li><p>When we match on the <code>OneToThree</code> proof term, information flows from the term level to the type level, refining the type of <code>as</code> in that branch.</p></li><li><p>The refined type of <code>as</code> then flows back down to the term level, restricting the shape the <code>HList</code> can take and refinine the set of patterns we have to match.</p></li></ul><p>Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an <code>HList</code> has an even number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This is a proof which itself has inductive structure: <code>EvenCons</code> takes a proof that <code>as</code> has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an <code>HList</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span> + +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Once again, this definition is completely exhaustive, and we can show that it works in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="kt">EvenNil</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.</p><h4><a name="proof-inference"></a>Proof inference</h4><p>While our definition of <code>pairUp</code> is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the <code>Even</code> proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.</p><p>Remember that typeclasses are functions from types to terms. As its happens, a value of type <code>Even as</code> can be mechanically produced from the structure of the type <code>as</code>. This suggests that we could use TMP to automatically generate <code>Even</code> proofs, and indeed, we can. In fact, it’s not at all complicated:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>We can now adjust our <code>pairUp</code> function to use <code>IsEven</code> instead of an explicit <code>Even</code> argument:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>This is essentially identical to its old definition, but by acquiring the proof via <code>IsEven</code> rather than passing it explicitly, we can call <code>pairUp</code> without having to construct a proof manually:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This is rather remarkable. Using TMP, we are able to get GHC to <em>automatically construct a proof that a list is even</em>, with no programmer guidance beyond writing the <code>IsEven</code> typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: <code>IsEven</code> is a function that accepts a type-level list and generates an <code>Even</code> proof term.</p><p>From this perspective, <strong>typeclasses are a way of specifying a proof search algorithm</strong> to the compiler. In the case of <code>IsEven</code>, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.</p><h3><a name="aside-gadts-versus-type-families"></a>Aside: GADTs versus type families</h3><p>Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.</p><p>Consider again the type of the <code>pairUp</code> function above (without the typeclass for simplicity):</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>We used both a GADT, <code>Even</code>, and a type family, <code>PairUp</code>. But we could have, in theory, used <em>only</em> a GADT and eliminated the type family altogether. Consider this variation on the <code>Even</code> proof term:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="p">((</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span></code></pre><p>This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of <code>as</code> as an “input” parameter and <code>bs</code> as an “output” parameter. The idea is that any <code>EvenPairs</code> proof relates both an even-length list type and its paired up equivalent:</p><ul><li><p><code>EvenNil</code> has type <code>EvenPairs '[] '[]</code>,</p></li><li><p><code>EvenCons EvenNil</code> has type <code>EvenPairs '[a, b] '[(a, b)]</code>,</p></li><li><p><code>EvenCons (EvenCons EvenNil)</code> has type <code>EvenPairs '[a, b, c, d] '[(a, b), (c, d)]</code>,</p></li><li><p>…and so on.</p></li></ul><p>This allows us to reformulate our <code>pairUp</code> type signature this way:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">bs</span></code></pre><p>The definition is otherwise unchanged. The <code>PairUp</code> type family is completely gone, because now <code>EvenPairs</code> itself defines the relation. In this way, GADTs can be used like type-level functions!</p><p>The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Kind</span><span class="w"> </span><span class="p">(</span><span class="kt">Constraint</span><span class="p">)</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span></code></pre><p>The idea here is that <code>IsEvenTF as</code> produces a constraint can only be satisfied if <code>as</code> has an even number of elements, since that’s the only way it will eventually reduce to <code>()</code>, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting <code>IsEvenTF as =&gt;</code> in a type signature successfully restricts <code>as</code> to be an even-length list, but it doesn’t allow us to write <code>pairUp</code>. To see why, we can try the following definition:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Unlike the version using the GADT, this version of <code>pairUp</code> is not considered exhaustive:</p><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil +</code></pre><p>This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by <code>IsEvenTF</code> provides no term-level evidence about the shape of <code>as</code>, so we can’t branch on it the way we can branch on the <code>Even</code> GADT.<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> (In a sense, <code>IsEvenTF</code> is doing <a href="/blog/2019/11/05/parse-don-t-validate/">validation, not parsing</a>.)</p><p>For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.</p><h3><a name="guiding-type-inference"></a>Guiding type inference</h3><p>So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.</p><p>However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.</p><p>To illustrate what that can look like, suppose we want to use TMP to generate an <code>HList</code> full of <code>()</code> values of an arbitrary length:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Testing in GHCi, we can see it behaves as desired:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[(), (), ()]</span> +<span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>Now suppose we write a function that accepts a list containing exactly one element and returns it:</p><pre><code class="pygments"><span class="nf">unsingleton</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[a]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unsingleton</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Naturally, we would expect these to compose without a hitch. If we write <code>unsingleton unitList</code>, our TMP should generate a list of length 1, and we should get back <code>()</code>. However, it may surprise you to learn that <em>isn’t</em>, in fact, what happens:<sup><a href="#footnote-6" id="footnote-ref-6-1">6</a></sup></p><pre><code>ghci&gt; unsingleton unitList + +error: + • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ + prevents the constraint ‘(UnitList '[a0])’ from being solved. + Probable fix: use a type annotation to specify what ‘a0’ should be. + These potential instances exist: + instance UnitList as =&gt; UnitList (() : as) +</code></pre><p>What went wrong? The type error says that <code>a0</code> is ambiguous, but it only lists a single matching <code>UnitList</code> instance—the one we want—so how can it be ambiguous which one to select?</p><p>The problem stems from the way we defined <code>UnitList</code>. When we wrote the instance</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span></code></pre><p>we said the first element of the type-level list must be <code>()</code>, so there’s nothing stopping someone from coming along and defining another instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="kt">Int</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>In that case, GHC would have no way to know which instance to pick. Nothing in the type of <code>unsingleton</code> forces the element in the list to have type <code>()</code>, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.</p><p>Of course, this isn’t what we want. The <code>UnitList</code> class is supposed to <em>always</em> return a list of <code>()</code> values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Here we’ve changed the instance so that it has the shape <code>UnitList (a ': as)</code>, with a type variable in place of the <code>()</code>, but we also added an equality constraint that forces <code>a</code> to be <code>()</code>. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unsingleton</span><span class="w"> </span><span class="n">unitList</span> +<span class="nb">()</span></code></pre><p>To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="o">&lt;</span><span class="n">constraints</span><span class="o">&gt;</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">C</span><span class="w"> </span><span class="o">&lt;</span><span class="n">types</span><span class="o">&gt;</span></code></pre><p>The part to the left of the <code>=&gt;</code> is known as the <em>instance context</em>, while the part to the right is known as the <em>instance head</em>. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, <strong>only the instance head matters, and the instance context is completely ignored</strong>. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.</p><p>This explains why our two <code>UnitList</code> instances behave differently:</p><ul><li><p>Given the instance head <code>UnitList (() ': as)</code>, GHC won’t select the instance unless it knows the first element of the list is <code>()</code>.</p></li><li><p>But given the instance head <code>UnitList (a ': as)</code>, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.</p></li></ul><p>After the <code>UnitList (a ': as)</code> instance is selected, GHC attempts to solve the constraints in the instance context, including the <code>a ~ ()</code> constraint. This <em>forces</em> <code>a</code> to be <code>()</code>, resolving the ambiguity and allowing type inference to proceed.</p><p>This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:</p><ul><li><p>If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.</p></li><li><p>But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell <em>you</em> what type this is,” effectively giving you a role in type inference itself.</p></li></ul><p>From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.</p><p>Given all of the above, consider again the definition of <code>IsEven</code> from earlier:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an <code>IsEven (a ': b0)</code> constraint, where <code>b0</code> is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an <code>IsEven '[a]</code> instance in the future.</p><p>To fix this, we can apply the same trick we used for <code>UnitList</code>, just in a slightly different way:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Again, the idea is to move the type information we <em>learn</em> from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can <strong>dramatically</strong> improve type inference in programs that make heavy use of TMP.</p><h3><a name="example-3-subtyping-constraints"></a>Example 3: Subtyping constraints</h3><p>At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at <a href="https://hasura.io/">Hasura</a>, I had the opportunity to design an internal parser combinator library that captures aspects of the <a href="https://graphql.org/">GraphQL</a> type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.</p><p>Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our <code>GQLKind</code> datatype has three cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLKind</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Both</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Input</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Output</span></code></pre><p>We use <code>DataKind</code>-promoted versions of this <code>GQLKind</code> type as a parameter to a <code>GQLType</code> GADT:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">TScalar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Both</span> +<span class="w"> </span><span class="kt">TInputObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">InputObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Input</span> +<span class="w"> </span><span class="kt">TIObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Output</span> +<span class="w"> </span><span class="c1">-- ...and so on...</span></code></pre><p>This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type <code>GQLType 'Input</code>, we can’t pass a <code>GQLType 'Both</code>, even though we really ought to be able to.</p><p>To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span></code></pre><p>The first case, <code>KRefl</code>, states that every kind is trivially a subkind of itself. The second case, <code>KBoth</code>, states that <code>Both</code> is a subkind of any kind at all. (This is a particularly literal example of <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">using a type to define axioms</a>.) The next step is to use TMP to implement proof inference:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KBoth</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span></code></pre><p>These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that <code>k</code> is a superkind of <code>'Input</code> or <code>'Output</code>, type inference will force them to be equal.</p><p>Using <code>IsSubKind</code>, we can easily resolve the problem described above. Rather than write a function with a type like this:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>…we simply use an <code>IsSubKind</code> constraint, instead:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now both <code>'Input</code> and <code>'Both</code> kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. <em>Consuming</em> the <code>SubKind</code> proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SelectionSet</span></code></pre><p>This type family is used to determine what a <code>GQLParser k a</code> actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>.</p><p>Fortunately, that is very easy to do using <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html">the <code>(:~:)</code> type from <code>Data.Type.Equality</code> in <code>base</code></a> to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">a</span></code></pre><p>Just as with any other GADT, <code>(:~:)</code> can be used to pack up type equalities and unpack them later; <code>a :~: b</code> just happens to be the GADT that corresponds precisely to the equality <code>a ~ b</code>. Using <code>(:~:)</code>, we can write a reusable proof that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>:</p><pre><code class="pygments"><span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="o">@</span><span class="kt">&#39;Input</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span></code></pre><p>This function is a very simple proof by cases, where <code>Refl</code> can be read as “Q.E.D.”:</p><ul><li><p>In the first case, matching on <code>KRefl</code> refines <code>k</code> to <code>'Input</code>, and <code>ParserInput 'Input</code> is <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li><li><p>Likewise, in the second case, matching on <code>KBoth</code> refines <code>k</code> to <code>'Both</code>, and <code>ParserInput 'Both</code> is also <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li></ul><p>This <code>inputParserInput</code> helper allows functions like <code>nullable</code>, which internally need <code>ParserInput k ~ InputValue</code>, to take the form</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nullable</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">inputParserInput</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ...implementation goes here... -}</span></code></pre><p>Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without <code>IsSubKind</code> doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!</p><h2><a name="wrapping-up-and-closing-thoughts"></a>Wrapping up and closing thoughts</h2><p>So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:</p><ul><li><p>Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.</p></li><li><p>Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.</p></li><li><p>Though I’ve called the technique “<em>typeclass</em> metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.</p></li><li><p>Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.</p></li></ul><p>The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.</p><p>Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.</p><p>It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:</p><ul><li><p>As mentioned earlier in this blog post, <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">the <code>GHC.Generics</code> module documentation</a> is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.</p></li><li><p>I have long believed that <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/">the GHC User’s Guide</a> is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.</p></li><li><p>Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the <a href="https://hackage.haskell.org/package/singletons">singletons</a> library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, <a href="https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf">the accompanying paper</a> is definitely worth a read if you’d like to go down that route.)</p></li></ul><p>Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.</p><ol class="footnotes"><li id="footnote-1"><p>Not to be confused with C++’s <a href="https://en.wikipedia.org/wiki/Template_metaprogramming"><em>template</em> metaprogramming</a>, though there are significant similarities between the two techniques. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>There have been proposals to introduce ordered instances, known in the literature as <a href="https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf"><em>instance chains</em></a>, but as of this writing, GHC does not implement them. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Note that this also preserves an important property of the Haskell type system, parametricity. A function like <code>id :: a -&gt; a</code> shouldn’t be allowed to do different things depending on which type is chosen for <code>a</code>, which our first version of <code>guardUnit</code> tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Short for <em>generalized algebraic datatypes</em>, which is a rather unhelpful name for actually understanding what they are or what they’re for. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for <code>IsEvenTF</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">exists</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">as&#39;</span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">)</span></code></pre><p>The type refinement provided by matching on <code>HCons</code> would be enough for the second case of <code>IsEvenTF</code> to be selected, which would provide an equality proof that <code>as</code> has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. <a href="#footnote-ref-5-1">↩</a></p></li><li id="footnote-6"><p>Actually, I’ve cheated a little bit here, because <code>unsingleton unitList</code> really does typecheck in GHCi under normal circumstances. That’s because <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules">the <code>ExtendedDefaultRules</code> extension</a> is enabled in GHCi by default, which defaults ambiguous type variables to <code>()</code>, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned <code>ExtendedDefaultRules</code> off to illustrate the problem. <a href="#footnote-ref-6-1">↩</a></p></li></ol></article>Names are not type safetyhttps://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/01 Nov 2020<article><p>Haskell programmers spend a lot of time talking about <em>type safety</em>. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published <a href="/blog/2019/11/05/parse-don-t-validate/">Parse, Don’t Validate</a> as an initial stab towards bridging that gap.</p><p>The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s <code>newtype</code> construct. The idea is simple enough—the <code>newtype</code> keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this <em>sounds</em> like a simple and straightforward path to type safety. For example, one might consider using a <code>newtype</code> declaration to define a type for an email address:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This technique can provide <em>some</em> value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct <em>kind</em> of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.</p><p>And names are not type safety.</p><h2><a name="intrinsic-and-extrinsic-safety"></a>Intrinsic and extrinsic safety</h2><p>To illustrate the difference between constructive data modeling (discussed at length in my <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">previous blog post</a>) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">One</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Two</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Three</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Four</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Five</span></code></pre><p>We could then write some functions to convert between <code>Int</code> and our <code>OneToFive</code> type:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">One</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Two</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Three</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Four</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Five</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">3</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">5</span></code></pre><p>This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="kt">Int</span></code></pre><p>Just as before, we can provide <code>toOneToFive</code> and <code>fromOneToFive</code> functions, with identical types:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">otherwise</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="p">(</span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">n</span></code></pre><p>If we put these declarations in their own module and choose not to export the <code>OneToFive</code> constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.</p><p>To see why, suppose we write a function that consumes a <code>OneToFive</code> value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"first"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"second"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"third"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fifth"</span></code></pre><p>The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an <code>Int</code>—after all, it <em>is</em> an <code>Int</code>. An <code>Int</code> can of course contain many other values besides <code>1</code> through <code>5</code>, so we are forced to add an error case to satisfy the exhaustiveness checker:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromOneToFive</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"first"</span> +<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"second"</span> +<span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"third"</span> +<span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fifth"</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: bad OneToFive value"</span></code></pre><p>In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:</p><ul><li><p>The constructive datatype captures its invariants in such a way that they are <em>accessible</em> to downstream consumers. This frees our <code>ordinal</code> function from worrying about handling illegal values, as they have been made unutterable.</p></li><li><p>The newtype wrapper provides a smart constructor that <em>validates</em> the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting <code>Int</code>s.</p></li></ul><p>Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of <code>error</code> has punched a hole right through our type system. If we were to add another constructor to our <code>OneToFive</code> datatype,<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> the version of <code>ordinal</code> that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.</p><p>All of this is a consequence of the fact that the constructive modeling is <em>intrinsically</em> type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent <code>6</code> using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an <code>Int</code>; its meaning is specified extrinsically via the <code>toOneToFive</code> smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.</p><h3><a name="revisiting-non-empty-lists"></a>Revisiting non-empty lists</h3><p>Our <code>OneToFive</code> datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the <code>NonEmpty</code> datatype I’ve repeatedly highlighted in recent blog posts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>It may be illustrative to imagine a version of <code>NonEmpty</code> represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Foldable</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Just as with <code>OneToFive</code>, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for <code>NonEmpty</code> was the ability to write a safe version of <code>head</code>, but the newtype version requires another assertion:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">x</span> +<span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines <code>NonEmpty</code>, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.</p><h2><a name="newtypes-as-tokens"></a>Newtypes as tokens</h2><p>If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes <em>can</em> provide a sort of safety, just a weaker one.</p><p>The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a <em>trust boundary</em> where internal invariants are enforced by restricting clients to a safe API.</p><p>We can use the <code>NonEmpty</code> example from above to illustrate how this works. We refrain from exporting the <code>NonEmpty</code> constructor, and we provide <code>head</code> and <code>tail</code> operations that we trust to never actually fail:</p><pre><code class="pygments"><span class="kr">module</span><span class="w"> </span><span class="nn">Data.List.NonEmpty.Newtype</span> +<span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">NonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">cons</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">nonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">head</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">tail</span> +<span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">cons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span> +<span class="nf">cons</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span> + +<span class="nf">tail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>Since the only way to construct or consume <code>NonEmpty</code> values is to use the functions in <code>Data.List.NonEmpty.Newtype</code>’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like <em>tokens</em>: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case <code>head</code> and <code>tail</code>, to obtain the values contained within.</p><p>This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid <code>NonEmpty []</code> value. For this reason, the newtype approach to type safety does not on its own constitute a <em>proof</em> that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a <code>Generic</code> instance for <code>NonEmpty</code>:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DeriveGeneric #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">GHC.Generics</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span></code></pre><p>However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">GHC</span><span class="o">.</span><span class="kt">Generics</span><span class="o">.</span><span class="n">to</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">K1</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>This is a particularly extreme example, since derived <code>Generic</code> instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived <code>Read</code> instance:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">read</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="s">"NonEmpty []"</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:</p><ul><li><p>All invariants must be made clear to maintainers of the trusted module. For simple types, such as <code>NonEmpty</code>, the invariant is self-evident, but for more sophisticated types, comments are not optional.</p></li><li><p>Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.</p></li><li><p>Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.</p></li><li><p>Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.</p></li></ul><p>In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.</p><p>In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.</p><h2><a name="other-newtype-use-abuse-and-misuse"></a>Other newtype use, abuse, and misuse</h2><p>The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:</p><ul><li><p>Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the <code>Sum</code> and <code>Product</code> newtypes from <code>Data.Monoid</code> provide useful <code>Monoid</code> instances for numeric types.</p></li><li><p>In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The <code>Flip</code> newtype from <code>Data.Bifunctor.Flip</code> is a simple example, flipping the arguments of a <code>Bifunctor</code> so the <code>Functor</code> instance may operate on the other side:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runFlip</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.</p></li><li><p>More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a <code>ByteString</code> containing a secret key may be wrapped in a newtype (with a <code>Show</code> instance omitted) to discourage code from accidentally logging or otherwise exposing it.</p></li></ul><p>All of these applications are good ones, but they have little to do with <em>type safety.</em> The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually <em>prevents</em> misuse; any part of the program may inspect the value at any time.</p><p>Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">unArgumentName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSONKey</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSONKey</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">Hashable</span><span class="p">,</span><span class="w"> </span><span class="kt">ToTxt</span><span class="p">,</span><span class="w"> </span><span class="kt">Lift</span><span class="p">,</span><span class="w"> </span><span class="kt">Generic</span><span class="p">,</span><span class="w"> </span><span class="kt">NFData</span><span class="p">,</span><span class="w"> </span><span class="kt">Cacheable</span><span class="w"> </span><span class="p">)</span></code></pre><p>This newtype is useless noise. Functionally, it is completely interchangeable with its underlying <code>Name</code> type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an <code>ArgumentName</code>, since the enclosing field name already makes its role clear.</p><p>Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:</p><ul><li><p>Primarily, types distinguish <em>functional</em> differences between values. A value of type <code>NonEmpty a</code> is <em>functionally</em> distinct from a value of type <code>[a]</code>, since it is fundamentally structurally different and permits additional operations. In this sense, types are <em>structural</em>; they describe what values <em>are</em> in the internal world of the programming language.</p></li><li><p>Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate <code>Distance</code> and <code>Duration</code> types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.</p></li></ul><p>Note that both these uses are <em>pragmatic</em>; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system <em>is</em> a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like <code>ArgumentName</code>.</p><p>If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use <code>Name</code>, but in situations where the different label adds genuine clarity, one can always use a type alias:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span></code></pre><p>Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.</p><h2><a name="final-thoughts-and-related-reading"></a>Final thoughts and related reading</h2><p>I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.</p><p>Newtypes are one particular mechanism of defining <em>wrapper types</em>, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.</p><p>The catalyst that got me to finally sit down and write this was the recently-published <a href="https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/">Tagged is not a Newtype</a>. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, <code>Tagged</code> <em>is</em> a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.</p><p>Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.</p><p>And a name is not type safety.</p><ol class="footnotes"><li id="footnote-1"><p>Admittedly rather unlikely given its name, but bear with me through the contrived example. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Types as axioms, or: playing god with static typeshttps://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/13 Aug 2020<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more openhttps://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/19 Jan 2020<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Parse, don’t validatehttps://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/05 Nov 2019<article><p>Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.</p><p>However, about a month ago, <a href="https://twitter.com/lexi_lambda/status/1182242561655746560">I was reflecting on Twitter</a> about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:</p><div style="text-align: center; font-size: larger"><strong>Parse, don’t validate.</strong></div><h2><a name="the-essence-of-type-driven-design"></a>The essence of type-driven design</h2><p>Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.</p><h3><a name="the-realm-of-possibility"></a>The realm of possibility</h3><p>One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:</p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Void</span></code></pre><p>Is it possible to implement <code>foo</code>? Trivially, the answer is <em>no</em>, as <code>Void</code> is a type that contains no values, so it’s impossible for <em>any</em> function to produce a value of type <code>Void</code>.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘head’: Patterns not matched: [] +</code></pre><p>This message is helpfully pointing out that our function is <em>partial</em>, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is <code>[]</code>, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.</p><h3><a name="turning-partial-functions-total"></a>Turning partial functions total</h3><p>To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the <code>head</code> function, and we’ll start with the simplest one.</p><h4><a name="managing-expectations"></a>Managing expectations</h4><p>As established, <code>head</code> is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the <code>Maybe</code> type:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span></code></pre><p>This buys us the freedom we need to implement <code>head</code>—it allows us to return <code>Nothing</code> when we discover we can’t produce a value of type <code>a</code> after all:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>Problem solved, right? For the moment, yes… but this solution has a hidden cost.</p><p>Returning <code>Maybe</code> is undoubtably convenient when we’re <em>implementing</em> <code>head</code>. However, it becomes significantly less convenient when we want to actually use it! Since <code>head</code> always has the potential to return <code>Nothing</code>, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">configDirsList</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">configDirsList</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">cacheDir</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="n">cacheDir</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"should never happen; already checked configDirs is non-empty"</span></code></pre><p>When <code>getConfigurationDirectories</code> retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use <code>head</code> in <code>main</code> to get the first element of the list, the <code>Maybe FilePath</code> result still requires us to handle a <code>Nothing</code> case that we know will never happen! This is terribly bad for several reasons:</p><ol><li><p>First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?</p></li><li><p>Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.</p></li><li><p>Finally, and worst of all, this code is a bug waiting to happen! What if <code>getConfigurationDirectories</code> were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update <code>main</code>, and suddenly the “impossible” error becomes not only possible, but probable.</p></li></ol><p>The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically <em>prove</em> the <code>Nothing</code> case impossible, then a modification to <code>getConfigurationDirectories</code> that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.</p><h4><a name="paying-it-forward"></a>Paying it forward</h4><p>Clearly, our modified version of <code>head</code> leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, <code>head</code> should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?</p><p>Let’s look at the original (partial) type signature for <code>head</code> again:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, <code>[a]</code>). Instead of weakening the return type, we can <em>strengthen</em> the argument type, eliminating the possibility of <code>head</code> ever being called on an empty list in the first place.</p><p>To do this, we need a type that represents non-empty lists. Fortunately, the existing <code>NonEmpty</code> type from <code>Data.List.NonEmpty</code> is exactly that. It has the following definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>Note that <code>NonEmpty a</code> is really just a tuple of an <code>a</code> and an ordinary, possibly-empty <code>[a]</code>. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the <code>[a]</code> component is <code>[]</code>, the <code>a</code> component must always be present. This makes <code>head</code> completely trivial to implement:<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Unlike before, GHC accepts this definition without complaint—this definition is <em>total</em>, not partial. We can update our program to use the new implementation:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">FilePath</span><span class="p">)</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">nonEmpty</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="p">)</span></code></pre><p>Note that the redundant check in <code>main</code> is now completely gone! Instead, we perform the check exactly once, in <code>getConfigurationDirectories</code>. It constructs a <code>NonEmpty a</code> from a <code>[a]</code> using the <code>nonEmpty</code> function from <code>Data.List.NonEmpty</code>, which has the following type:</p><pre><code class="pygments"><span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>The <code>Maybe</code> is still there, but this time, we handle the <code>Nothing</code> case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a <code>NonEmpty FilePath</code> value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type <code>NonEmpty a</code> as being like a value of type <code>[a]</code>, plus a <em>proof</em> that the list is non-empty.</p><p>By strengthening the type of the argument to <code>head</code> instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:</p><ul><li><p>The code has no redundant checks, so there can’t be any performance overhead.</p></li><li><p>Furthermore, if <code>getConfigurationDirectories</code> changes to stop checking that the list is non-empty, its return type must change, too. Consequently, <code>main</code> will fail to typecheck, alerting us to the problem before we even run the program!</p></li></ul><p>What’s more, it’s trivial to recover the old behavior of <code>head</code> from the new one by composing <code>head</code> with <code>nonEmpty</code>:</p><pre><code class="pygments"><span class="nf">head&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fmap</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">nonEmpty</span></code></pre><p>Note that the inverse is <em>not</em> true: there is no way to obtain the new version of <code>head</code> from the old one. All in all, the second approach is superior on all axes.</p><h3><a name="the-power-of-parsing"></a>The power of parsing</h3><p>You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:</p><pre><code class="pygments"><span class="nf">validateNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span> + +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="n">xs</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span></code></pre><p>These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: <code>validateNonEmpty</code> always returns <code>()</code>, the type that contains no information, but <code>parseNonEmpty</code> returns <code>NonEmpty a</code>, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but <code>parseNonEmpty</code> gives the caller access to the information it learned, while <code>validateNonEmpty</code> just throws it away.</p><p>These two functions elegantly illustrate two different perspectives on the role of a static type system: <code>validateNonEmpty</code> obeys the typechecker well enough, but only <code>parseNonEmpty</code> takes full advantage of it. If you see why <code>parseNonEmpty</code> is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of <code>parseNonEmpty</code>’s name. Is it really <em>parsing</em> anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe <code>parseNonEmpty</code> is a bona-fide parser (albeit a particularly simple one).</p><p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and <code>parseNonEmpty</code> is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.</p><p>Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:</p><ul><li><p>The <a href="https://hackage.haskell.org/package/aeson">aeson</a> library provides a <code>Parser</code> type that can be used to parse JSON data into domain types.</p></li><li><p>Likewise, <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> provides a set of parser combinators for parsing command-line arguments.</p></li><li><p>Database libraries like <a href="https://hackage.haskell.org/package/persistent">persistent</a> and <a href="https://hackage.haskell.org/package/postgresql-simple">postgresql-simple</a> have a mechanism for parsing values held in an external data store.</p></li><li><p>The <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.</p></li></ul><p>The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.</p><p>One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the <code>NonEmpty</code> example above: if the parsing and processing logic go out of sync, the program will fail to even compile.</p><h3><a name="the-danger-of-validation"></a>The danger of validation</h3><p>Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?</p><p>Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the <a href="http://langsec.org">language-theoretic security</a> field calls <em>shotgun parsing</em>. In the 2016 paper, <a href="http://langsec.org/papers/langsec-cwes-secdev2016.pdf">The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them</a>, its authors provide the following definition:</p><blockquote><p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.</p></blockquote><p>They go on to explain the problems inherent to such validation techniques:</p><blockquote><p>Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.</p></blockquote><p>In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.</p><p>It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.</p><p>Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p><h2><a name="parsing-not-validating-in-practice"></a>Parsing, not validating, in practice</h2><p>So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”</p><p>My advice: focus on the datatypes.</p><p>Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a <code>Map</code>. Adjust your function’s type signature to accept a <code>Map</code> instead of a list of tuples, and implement it as you normally would.</p><p>Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to <code>Map</code>, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of <code>checkNoDuplicateKeys</code>:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span></code></pre><p>Now the check <em>cannot</em> be omitted, since its result is actually necessary for the program to proceed!</p><p>This hypothetical scenario highlights two simple ideas:</p><ol><li><p><strong>Use a data structure that makes illegal states unrepresentable.</strong> Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.</p></li><li><p><strong>Push the burden of proof upward as far as possible, but no further.</strong> Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.</p></li></ol><p>In other words, write functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!</p><p>Here are a handful of additional points of advice, arranged in no particular order:</p><ul><li><p><strong>Let your datatypes inform your code, don’t let your code control your datatypes.</strong> Avoid the temptation to just stick a <code>Bool</code> in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.</p></li><li><p><strong>Treat functions that return <code>m ()</code> with deep suspicion.</strong> Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.</p></li><li><p><strong>Don’t be afraid to parse data in multiple passes.</strong> Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p></li><li><p><strong>Avoid denormalized representations of data, <em>especially</em> if it’s mutable.</strong> Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.</p><ul><li><p><strong>Keep denormalized representations of data behind abstraction boundaries.</strong> If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.</p></li></ul></li><li><p><strong>Use abstract datatypes to make validators “look like” parsers.</strong> Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract <code>newtype</code> with a smart constructor to “fake” a parser from a validator.</p></li></ul><p>As always, use your best judgement. It probably isn’t worth breaking out <a href="https://hackage.haskell.org/package/singletons">singletons</a> and refactoring your entire application just to get rid of a single <code>error "impossible"</code> call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.</p><h2><a name="recap-reflection-and-related-reading"></a>Recap, reflection, and related reading</h2><p>That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.</p><p>None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about <em>process</em>. My hope is that this is a small step in that direction.</p><p>Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post <a href="https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html">Type Safety Back and Forth</a>. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper <a href="https://kataskeue.com/gdp.pdf">Ghosts of Departed Proofs</a>, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.</p><p>As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit <em>any</em> value. These aren’t “real” values (unlike <code>null</code> in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that <a href="https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf">Fast and Loose Reasoning is Morally Correct</a>. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In fact, <code>Data.List.NonEmpty</code> already provides a <code>head</code> function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Empathy and subjective experience in programming languageshttps://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/https://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/19 Oct 2019<article><p>A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.</p><p>Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?</p><p>I think about that question a lot.</p><h2><a name="2015-called-and-they-want-their-dress-back"></a>2015 called, and they want their dress back</h2><p>Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.</p><p>However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into <em>Star Wars</em>. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.</p><p>Take 2015’s phenomenon of “<a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did <em>this</em>—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.</p><h3><a name="when-something-objective-isn-t"></a>When something objective isn’t</h3><p>Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.</p><p>The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.</p><p>In the case of the dress, whether you <a href="https://en.wikipedia.org/wiki/Yanny_or_Laurel">heard “yanny” or “laurel,”</a> or whether you believe the <em>Sonic</em> games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?</p><p>I think the answer is absolutely, unequivocally <em>yes</em>.</p><h2><a name="subjectivity-in-programming-and-in-programming-languages-specifically"></a>Subjectivity in programming, and in programming languages specifically</h2><p>Quick question: which is better, functional or imperative programming?</p><p>My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?</p><p>Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?</p><p>I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> but I did happen to find a link to <a href="https://news.ycombinator.com/item?id=21282647">a recent discussion</a> on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?</p><p>In a <a href="https://news.ycombinator.com/item?id=21284383">branch of the discussion</a>, one user writes:</p><blockquote><blockquote><p>Haskell is great for business and great in production</p></blockquote><p>I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.</p><p>I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.</p></blockquote><p>The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:</p><blockquote><p>I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.</p><p>[…]</p><p>From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.</p></blockquote><p>Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:</p><blockquote><p>Haskell gives one plenty of rope to hang himself on complexity.</p><p>So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.</p></blockquote><p>Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:</p><blockquote><p>Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.</p><p>[…]</p><p>The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.</p><p>Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.</p></blockquote><p>That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?</p><h3><a name="the-unsatisfying-subjective-reality-of-programming-languages"></a>The unsatisfying subjective reality of programming languages</h3><p>You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?</p><p><em><strong>No!</strong></em></p><p>These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, <em>why?</em> Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.</p><p>While <a href="https://news.ycombinator.com/item?id=21284317">one commenter</a> in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” <a href="https://news.ycombinator.com/item?id=21284540">another</a> says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.</p><p>Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t <em>get it.</em> There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.</p><h3><a name="empathy-and-how-bad-results-come-from-good-intentions"></a>Empathy, and how bad results come from good intentions</h3><p>I’ll admit that these kinds of discussions aren’t <em>always</em> fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.</p><p>Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.</p><p>Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:</p><blockquote><p>Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.</p></blockquote><p>I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?</p><p>If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.</p><p>Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.</p><p>To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:</p><blockquote><p>It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.</p><p>Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.</p></blockquote><p>When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective <strong>without</strong> invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about <em>your own</em> experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.</p><p>It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.</p><p>Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be <em>happier.</em></p><ol class="footnotes"><li id="footnote-1"><p>This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is <em>blah</em>,” but you deserve better. So you, uh, get a <em>meta</em> snarky footnote instead. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Which, to be entirely fair, may well be as subjective as anything else in this blog post. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Demystifying MonadBaseControlhttps://lexi-lambda.github.io/blog/2019/09/07/demystifying-monadbasecontrol/https://lexi-lambda.github.io/blog/2019/09/07/demystifying-monadbasecontrol/07 Sep 2019<article><p><a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl"><code>MonadBaseControl</code> from the <code>monad-control</code> package</a> is a confusing typeclass, and its methods have complicated types. For many people, it’s nothing more than scary, impossible-to-understand magic that is, for some reason, needed when lifting certain kinds of operations. Few resources exist that adequately explain how, why, and when it works, which sadly seems to have resulted in some <a href="https://en.wikipedia.org/wiki/Fear,_uncertainty,_and_doubt">FUD</a> about its use.</p><p>There’s no doubt that the machinery of <code>MonadBaseControl</code> is complex, and the role it plays in practice is often subtle. However, its essence is actually much simpler than it appears, and I promise it can be understood by mere mortals. In this blog post, I hope to provide a complete survey of <code>MonadBaseControl</code>—how it works, how it’s designed, and how it can go wrong—in a way that is accessible to anyone with a firm grasp of monads and monad transformers. To start, we’ll motivate <code>MonadBaseControl</code> by reinventing it ourselves.</p><h2><a name="the-higher-order-action-problem"></a>The higher-order action problem</h2><p>Say we have a function with the following type:<sup><a href="#footnote-0" id="footnote-ref-0-1">1</a></sup></p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>If we have an action built from a transformer stack like</p><pre><code class="pygments"><span class="nf">bar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span></code></pre><p>then we might wish to apply <code>foo</code> to <code>bar</code>, but that is ill-typed, since <code>IO</code> is not the same as <code>StateT X IO</code>. In cases like these, we often use <code>lift</code>, but it’s not good enough here: <code>lift</code> <em>adds</em> a new monad transformer to an action, but here we need to <em>remove</em> a transformer. So we need a function with a type like this:</p><pre><code class="pygments"><span class="nf">unliftState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">Y</span></code></pre><p>However, if you think about that type just a little bit, it’s clear something’s wrong: it throws away information, namely the state. You may remember that a <code>StateT X IO Y</code> action is equivalent to a function of type <code>X -&gt; IO (Y, X)</code>, so our hypothetical <code>unliftState</code> function has two problems:</p><ol><li><p>We have no <code>X</code> to use as the initial state.</p></li><li><p>We’ll lose any modifications <code>bar</code> made to the state, since the result type is just <code>Y</code>, not <code>(Y, X)</code>.</p></li></ol><p>Clearly, we’ll need something more sophisticated, but what?</p><h2><a name="a-na-ve-solution"></a>A naïve solution</h2><p>Given that <code>foo</code> doesn’t know anything about the state, we can’t easily thread it through <code>foo</code> itself. However, by using <code>runStateT</code> explicitly, we could do some of the state management ourselves:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">v</span></code></pre><p>Do you see what’s going on there? It’s not actually very complicated: we get the current state, then pass it as the initial state to <code>runStateT</code>. This produces an action <code>IO (a, s)</code> that has <em>closed over</em> the current state. We can pass that action to <code>foo</code> without issue, since <code>foo</code> is polymorphic in the action’s return type. Finally, all we have to do is <code>put</code> the modified state back into the enclosing <code>StateT</code> computation, and we can get on with our business.</p><p>That strategy works okay when we only have one monad transformer, but it gets hairy quickly as soon as we have two or more. For example, if we had <code>baz :: ExceptT X (StateT Y IO) Z</code>, then we <em>could</em> do the same trick by getting the underlying</p><pre><code class="pygments"><span class="kt">Y</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="kt">Z</span><span class="p">,</span><span class="w"> </span><span class="kt">Y</span><span class="p">)</span></code></pre><p>function, closing over the state, restoring it, and doing the appropriate case analysis to re-raise any <code>ExceptT</code> errors, but that’s a lot of work to do for every single function! What we’d like to do instead is somehow abstract over the pattern we used to write <code>foo'</code> in a way that scales to arbitrary monad transformers.</p><h2><a name="the-essence-of-monadbasecontrol"></a>The essence of <code>MonadBaseControl</code></h2><p>To build a more general solution for “unlifting” arbitrary monad transformers, we need to start thinking about monad transformer state. The technique we used to implement <code>foo'</code> operated on the following process:</p><ol><li><p>Capture the action’s input state and close over it.</p></li><li><p>Package up the action’s output state with its result and run it.</p></li><li><p>Restore the action’s output state into the enclosing transformer.</p></li><li><p>Return the action’s result.</p></li></ol><p>For <code>StateT s</code>, it turns out that the input state and output state are both <code>s</code>, but other monad transformers have state, too. Consider the input and output state for the following common monad transformers:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>StateT s m a</code></td> + <td><code>s -&gt; m (a, s)</code></td> + <td><code>s</code></td> + <td><code>s</code></td> + </tr> + <tr> + <td><code>ReaderT r m a</code></td> + <td><code>r -&gt; m a</code></td> + <td><code>r</code></td> + <td><code>()</code></td> + </tr> + <tr> + <td><code>WriterT w m a</code></td> + <td><code>m (a, w)</code></td> + <td><code>()</code></td> + <td><code>w</code></td> + </tr> + </table> +</div><p>Notice how the input state is whatever is to the left of the <code>-&gt;</code>, while the output state is whatever extra information gets produced alongside the result. Using the same reasoning, we can also deduce the input and output state for compositions of multiple monad transformers, such as the following:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>ReaderT r (WriterT w m) a</code></td> + <td><code>r -&gt; m (a, w)</code></td> + <td><code>r</code></td> + <td><code>w</code></td> + </tr> + <tr> + <td><code>StateT s (ReaderT r m) a</code></td> + <td><code>r -&gt; s -&gt; m (a, s)</code></td> + <td><code>(r, s)</code></td> + <td><code>s</code></td> + </tr> + <tr> + <td><code>WriterT w (StateT s m) a</code></td> + <td><code>s -&gt; m ((a, w), s)</code></td> + <td><code>s</code></td> + <td><code>(w, s)</code></td> + </tr> + </table> +</div><p>Notice that when monad transformers are composed, their states are composed, too. This is useful to keep in mind, since our goal is to capture the four steps above in a typeclass, polymorphic in the state of the monad transformers we need to lift through. At minimum, we need two new operations: one to capture the input state and close over it (step 1) and one to restore the output state (step 3). One class we might come up with could look like this:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>If we can write instances of that typeclass for various transformers, we can use the class’s operations to implement <code>foo'</code> in a generic way that works with any combination of them:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">v</span></code></pre><p>So how do we implement those instances? Let’s start with <code>IO</code>, since that’s the base case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;&amp;&gt;</span><span class="w"> </span><span class="p">(,</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Not very exciting. The <code>StateT s</code> instance, on the other hand, is significantly more interesting:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(,)</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&lt;*&gt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span></code></pre><p><strong>This instance alone includes most of the key ideas behind <code>MonadBaseControl</code>.</strong> There’s a lot going on, so let’s break it down, step by step:</p><ol><li><p>Start by examining the definitions of <code>InputState</code> and <code>OutputState</code>. Are they what you expected? You’d be forgiven for expecting the following:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">s</span> +<span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">s</span></code></pre><p>After all, that’s what we wrote in the table, isn’t it?</p><p>However, if you give it a try, you’ll find it doesn’t work. <code>InputState</code> and <code>OutputState</code> must capture the state of the <em>entire</em> monad, not just a single transformer layer, so we have to combine the <code>StateT s</code> state with the state of the underlying monad. In the simplest case we get</p><pre><code class="pygments"><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>which is boring, but in a more complex case, we need to get something like this:</p><pre><code class="pygments"><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="kt">IO</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="nb">()</span><span class="p">))</span></code></pre><p>Therefore, <code>InputState (StateT s m)</code> combines <code>s</code> with <code>InputState m</code> in a tuple, and <code>OutputState</code> does the same.</p></li><li><p>Moving on, take a look at <code>captureInputState</code> and <code>closeOverInputState</code>. Just as <code>InputState</code> and <code>OutputState</code> capture the state of the entire monad, these functions need to be inductive in the same way.</p><p><code>captureInputState</code> acquires the current state using <code>get</code>, and it combines it with the remaining monadic state using <code>lift captureInputState</code>. <code>closeOverInputState</code> uses the captured state to peel off the outermost <code>StateT</code> layer, then calls <code>closeOverInputState</code> recursively to peel off the rest of them.</p></li><li><p>Finally, <code>restoreOutputState</code> restores the state of the underlying monad stack, then restores the <code>StateT</code> state, ensuring everything ends up back the way it’s supposed to be.</p></li></ol><p>Take the time to digest all that—work through it yourself if you need to—as it’s a dense piece of code. Once you feel comfortable with it, take a look at the instances for <code>ReaderT</code> and <code>WriterT</code> as well:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(,)</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="o">&lt;*&gt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span></code></pre><p>Make sure you understand these instances, too. It should be easier this time, since they share most of their structure with the <code>StateT</code> instance, but note the asymmetry that arises from the differing input and output states. (It may even help to try and write these instances yourself, focusing on the types whenever you get stuck.)</p><p>If you feel alright with them, then congratulations: you’re already well on your way to grokking <code>MonadBaseControl</code>!</p><h3><a name="hiding-the-input-state"></a>Hiding the input state</h3><p>So far, our implementation of <code>MonadBaseControl</code> works, but it’s actually slightly more complicated than it needs to be. As it happens, all valid uses of <code>MonadBaseControl</code> will always end up performing the following pattern:</p><pre><code class="pygments"><span class="nf">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureInputState</span> +<span class="kr">let</span><span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span></code></pre><p>That is, we close over the input state as soon as we capture it. We can therefore combine <code>captureInputState</code> and <code>closeOverInputState</code> into a single function:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">))</span></code></pre><p>What’s more, we no longer need the <code>InputState</code> associated type at all! This is an improvement, since it simplifies the API and removes the possibility for any misuse of the input state, since it’s never directly exposed. On the other hand, it has a more complicated type: it produces a monadic action <em>that returns another monadic action</em>. This can be a little more difficult to grok, which is why I presented the original version first, but it may help to consider how the above type arises naturally from the following definition:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">closeOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">captureInputState</span></code></pre><p>Let’s update the <code>MonadBaseControl</code> class to incorporate this simplification:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>We can then update all the instances to use the simpler API by simply fusing the definitions of <code>captureInputState</code> and <code>closeOverInputState</code> together:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">&lt;&amp;&gt;</span><span class="w"> </span><span class="p">(,</span><span class="w"> </span><span class="nb">()</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span></code></pre><p>This is already very close to a full <code>MonadBaseControl</code> implementation. The <code>captureAndCloseOverInputState</code> implementations are getting a little out of hand, but bear with me—they’ll get simpler before this blog post is over.</p><h3><a name="coping-with-partiality"></a>Coping with partiality</h3><p>Our <code>MonadBaseControl</code> class now works with <code>StateT</code>, <code>ReaderT</code>, and <code>WriterT</code>, but one transformer we haven’t considered is <code>ExceptT</code>. Let’s try to extend our table from before with a row for <code>ExceptT</code>:</p><div class="table-wrapper"> + <table class="no-line-wrapping"> + <thead><tr> + <th>transformer</th> + <th>representation</th> + <th>input state</th> + <th>output state</th> + </tr></thead> + <tr> + <td><code>ExceptT e m a</code></td> + <td><code>m (Either e a)</code></td> + <td><code>()</code></td> + <td><code>???</code></td> + </tr> + </table> +</div><p>Hmm… what <em>is</em> the output state for <code>ExceptT</code>?</p><p>The answer can’t be <code>e</code>, since we might not end up with an <code>e</code>—the computation might not fail. <code>Maybe e</code> would be closer… could that work?</p><p>Well, let’s try it. Let’s write a <code>MonadBaseControl</code> instance for <code>ExceptT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">OutputState</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">((</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">),</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">m&#39;</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">s&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">ss&#39;</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Sadly, the above implementation doesn’t typecheck; it is rejected with the following type error:</p><pre><code>• Couldn't match type ‘Either e a’ with ‘(a, Maybe e)’ + Expected type: m (b ((a, Maybe e), OutputState m)) + Actual type: m (b (Either e a, OutputState m)) +• In the second argument of ‘($)’, namely + ‘captureAndCloseOverInputState (runExceptT m)’ + In a stmt of a 'do' block: + m' &lt;- lift $ captureAndCloseOverInputState (runExceptT m) + In the expression: + do m' &lt;- lift $ captureAndCloseOverInputState (runExceptT m) + return do ((v, s'), ss') &lt;- m' + pure (v, (s', ss')) +</code></pre><p>We promised a <code>(a, Maybe e)</code>, but we have an <code>Either e a</code>, and there’s certainly no way to get the former from the latter. Are we stuck? (If you’d like, take a moment to think about how you’d solve this type error before moving on, as it may be helpful for understanding the following solution.)</p><p>The fundamental problem here is <em>partiality</em>. The type of the <code>captureAndCloseOverInputState</code> method always produces an action in the base monad that includes an <code>a</code> <em>in addition</em> to some other output state. But <code>ExceptT</code> is different: when it an error is raised, it doesn’t produce an <code>a</code> at all—it only produces an <code>e</code>. Therefore, as written, it’s impossible to give <code>ExceptT</code> a <code>MonadBaseControl</code> instance.</p><p>Of course, we’d very much <em>like</em> to give <code>ExceptT</code> a <code>MonadBaseControl</code> instance, so that isn’t very satisfying. Somehow, we need to change <code>captureAndCloseOverInputState</code> so that it doesn’t always need to produce an <code>a</code>. There are a few ways we could accomplish that, but an elegant way to do it is this:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>We’ve replaced the old <code>OutputState</code> associated type with a new <code>WithOutputState</code> type, and the key difference between them is that <code>WithOutputState</code> describes the type of a <em>combination</em> of the result (of type <code>a</code>) and the output state, rather than describing the type of the output state alone. For total monad transformers like <code>StateT</code>, <code>ReaderT</code>, and <code>WriterT</code>, <code>WithOutputState m a</code> will just be a tuple of the result value and the output state, the same as before. For example, here’s an updated <code>MonadBaseControl</code> instance for <code>StateT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">a</span></code></pre><p>Before we consider how this helps us with <code>ExceptT</code>, let’s pause for a moment and examine the revised <code>StateT</code> instance in detail, as there are some new things going on here:</p><ul><li><p>Take a close look at the definition of <code>WithOutputState (StateT s m) a</code>. Note that we’ve defined it to be <code>WithOutputState m (a, s)</code>, <em>not</em> <code>(WithOutputState m a, s)</code>. Consider, for a moment, the difference between these types. Can you see why we used the former, not the latter?</p><p>If it’s unclear to you, that’s okay—let’s illustrate the difference with an example. Consider two similar monad transformer stacks:</p><pre><code class="pygments"><span class="nf">m1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="n">a</span> +<span class="nf">m2</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kt">IO</span><span class="p">)</span><span class="w"> </span><span class="n">a</span></code></pre><p>Both these stacks contain <code>StateT</code> and <code>ExceptT</code>, but they are layered in a different order. What’s the difference? Well, consider what <code>m1</code> and <code>m2</code> return once fully unwrapped:</p><pre><code class="pygments"><span class="nf">runExceptT</span><span class="w"> </span><span class="p">(</span><span class="n">runStateT</span><span class="w"> </span><span class="n">m1</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))</span> +<span class="nf">runStateT</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m2</span><span class="p">)</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span></code></pre><p>These results are meaningfully different: in <code>m1</code>, the state is discarded if an error is raised, but in <code>m2</code>, the final state is always returned, even if the computation is aborted. What does this mean for <code>WithOutputState</code>?</p><p>Here’s the important detail: <strong>the state is discarded when <code>ExceptT</code> is “inside” <code>StateT</code>, not the other way around.</strong> This can be counterintuitive, since the <code>s</code> ends up <em>inside</em> the <code>Either</code> when the <code>StateT</code> constructor is on the <em>outside</em> and vice versa. This is really just a property of how monad transformers compose, not anything specific to <code>MonadBaseControl</code>, so an explanation of why this happens is outside the scope of this blog post, but the relevant insight is that the <code>m</code> in <code>StateT s m a</code> controls the eventual action’s output state.</p><p>If we had defined <code>WithOutputState (StateT s m) a</code> to be <code>(WithOutputState m a, s)</code>, we’d be in a pickle, since <code>m</code> would be unable to influence the presence of <code>s</code> in the output state. Therefore, we have no choice but to use <code>WithOutputState m (a, s)</code>. (If you are still confused by this, try it yourself; you’ll find that there’s no way to make the other definition typecheck.)</p></li><li><p>Now that we’ve developed an intuitive understanding of why <code>WithOutputState</code> must be defined the way it is, let’s look at things from another perspective. Consider the type of <code>runStateT</code> once more:</p><pre><code class="pygments"><span class="nf">runStateT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span></code></pre><p>Note that the result type is <code>m (a, s)</code>, with the <code>m</code> on the outside. As it happens, this correspondence simplifies the definition of <code>captureAndCloseOverInputState</code>, since we no longer have to do any fiddling with its result—it’s already in the proper shape, so we can just return it directly.</p></li><li><p>Finally, this instance illustrates an interesting change to <code>restoreOutputState</code>. Since the <code>a</code> is now packed inside the <code>WithOutputState m a</code> value, the caller of <code>captureAndCloseOverInputState</code> needs some way to get the <code>a</code> back out! Conveniently, <code>restoreOutputState</code> can play that role, both restoring the output state and unpacking the result.</p><p>Even ignoring partial transformers like <code>ExceptT</code>, this is an improvement over the old API, as it conveniently prevents the programmer from forgetting to call <code>restoreOutputState</code>. However, as we’ll see shortly, it is much more than a convenience: once <code>ExceptT</code> comes into play, it is essential!</p></li></ul><p>With those details addressed, let’s return to <code>ExceptT</code>. Using the new interface, writing an instance for <code>ExceptT</code> is not only possible, it’s actually rather easy:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">either</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="p">(</span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="p">)</span></code></pre><p>This instance illustrates why it’s so crucial that <code>restoreOutputState</code> have the aforementioned dual role: it must handle the case where no <code>a</code> exists at all! In the case of <code>ExceptT</code>, it restores the state in the enclosing monad by re-raising an error.</p><p>Now all that’s left to do is update the other instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="p">(</span><span class="n">runWriterT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="n">ss</span> +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">a</span></code></pre><p>Finally, we can update our lifted variant of <code>foo</code> to use the new interface so it will work with transformer stacks that include <code>ExceptT</code>:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span></code></pre><p>At this point, it’s worth considering something: although getting the <code>MonadBaseControl</code> class and instances right was a lot of work, the resulting <code>foo'</code> implementation is actually incredibly simple. That’s a good sign, since we only have to write the <code>MonadBaseControl</code> instances once (in a library), but we have to write functions like <code>foo'</code> quite often.</p><h2><a name="scaling-to-the-real-monadbasecontrol"></a>Scaling to the real <code>MonadBaseControl</code></h2><p>The <code>MonadBaseControl</code> class we implemented in the previous section is complete. It is a working, useful class that is equivalent in power to <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl">the “real” <code>MonadBaseControl</code> class in the <code>monad-control</code> library</a>. However, if you compare the two, you’ll notice that the version in <code>monad-control</code> looks a little bit different. What gives?</p><p>Let’s compare the two classes side by side:</p><pre><code class="pygments"><span class="c1">-- ours</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="w"> </span><span class="n">restoreOutputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WithOutputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> + +<span class="c1">-- theirs</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Let’s start with the similarities, since those are easy:</p><ul><li><p>Our <code>WithOutputState</code> associated type is precisely equivalent to their <code>StM</code> associated type, they just use a (considerably) shorter name.</p></li><li><p>Likewise, our <code>restoreOutputState</code> method is precisely equivalent to their <code>restoreM</code> method, simply under a different name.</p></li></ul><p>That leaves <code>captureAndCloseOverInputState</code> and <code>liftBaseWith</code>. Those two methods both do similar things, but they aren’t identical, and that’s where all the differences lie. To understand <code>liftBaseWith</code>, let’s start by inlining the definition of the <code>RunInBase</code> type alias so we can see the fully-expanded type:</p><pre><code class="pygments"><span class="nf">liftBaseWith</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">((</span><span class="n">forall</span><span class="w"> </span><span class="n">c</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">c</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>That type is complicated! However, if we break it down, hopefully you’ll find it’s not as scary as it first appears. Let’s reimplement the <code>foo'</code> example from before using <code>liftBaseWith</code> to show how this version of <code>MonadBaseControl</code> works:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>This is, in some ways, superficially similar to the version we wrote using our version of <code>MonadBaseControl</code>. Just like in our version, we capture the input state, apply <code>foo</code> in the <code>IO</code> monad, then restore the state. But what exactly is doing the state capturing, and what is <code>runInBase</code>?</p><p>Let’s start by adding a type annotation to <code>runInBase</code> to help make it a little clearer what’s going on:</p><pre><code class="pygments"><span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">b</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>That type should look sort of recognizable. If we replace <code>StM</code> with <code>WithOutputState</code>, then we get a type that looks very similar to that of our original <code>closeOverInputState</code> function, except it doesn’t need to take the input state as an argument. How does that work?</p><p>Here’s the trick: <code>liftBaseWith</code> starts by capturing the input state, just as before. However, it then builds a function, <code>runInBase</code>, which is like <code>closeOverInputState</code> partially-applied to the input state it captured. It hands that function to us, and we’re free to apply it to <code>m</code>, which produces the <code>IO (StM m a)</code> action we need, and we can now pass that action to <code>foo</code>. The result is returned in the outer monad, and we restore the state using <code>restoreM</code>.</p><h3><a name="sharing-the-input-state"></a>Sharing the input state</h3><p>At first, this might seem needlessly complicated. When we first started, we separated capturing the input state and closing over it into two separate operations (<code>captureInputState</code> and <code>closeOverInputState</code>), but we eventually combined them so that we could keep the input state hidden. Why does <code>monad-control</code> split them back into two operations again?</p><p>As it turns out, when lifting <code>foo</code>, there’s no advantage to the more complicated API of <code>monad-control</code>. In fact, we could implement our <code>captureAndCloseOverInputState</code> operation in terms of <code>liftBaseWith</code>, and we could use that to implement <code>foo'</code> the same way we did before:</p><pre><code class="pygments"><span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="nf">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> + +<span class="nf">foo&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">foo&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">m&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span></code></pre><p>However, that approach has a downside once we need to lift more complicated functions. <code>foo</code> is exceptionally simple, as it only accepts a single input argument, but what if we wanted to lift a more complicated function that took <em>two</em> monadic arguments, such as this one:</p><pre><code class="pygments"><span class="nf">bar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>We could implement that by calling <code>captureAndCloseOverInputState</code> twice, like this:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">ma&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">ma</span> +<span class="w"> </span><span class="n">mb&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">captureAndCloseOverInputState</span><span class="w"> </span><span class="n">mb</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">bar</span><span class="w"> </span><span class="n">ma&#39;</span><span class="w"> </span><span class="n">mb&#39;</span><span class="p">)</span></code></pre><p>However, that would capture the monadic state twice, which is rather inefficient. By using <code>liftBaseWith</code>, the state capturing is done just once, and it’s shared between all calls to <code>runInBase</code>:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>By providing a “running” function (<code>runInBase</code>) instead of direct access to the input state, <code>liftBaseWith</code> allows sharing the captured input state between multiple actions without exposing it directly.</p><h3><a name="sidebar-continuation-passing-and-impredicativity"></a>Sidebar: continuation-passing and impredicativity</h3><p>One last point before we move on: although the above explains why <code>captureAndCloseOverInputState</code> is insufficient, you may be left wondering why <code>liftBaseWith</code> can’t just <em>return</em> <code>runInBase</code>. Why does it need to be given a continuation? After all, it would be nicer if we could just write this:</p><pre><code class="pygments"><span class="nf">bar&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">bar&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">askRunInBase</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="o">=&lt;&lt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">))</span></code></pre><p>To understand the problem with a hypothetical <code>askRunInBase</code> function, remember that the type of <code>runInBase</code> is polymorphic:</p><pre><code class="pygments"><span class="nf">runInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This is important, since if you need to lift a function with a type like</p><pre><code class="pygments"><span class="nf">baz</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)</span></code></pre><p>then you’ll want to instantiate that <code>a</code> variable with two different types. We’d need to retain that power in <code>askRunInBase</code>, so it would need to have the following type:</p><pre><code class="pygments"><span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span></code></pre><p>Sadly, that type is illegal in Haskell. Type constructors must be applied to monomorphic types, but in the above type signature, <code>m</code> is applied to a polymorphic type.<sup><a href="#footnote-1" id="footnote-ref-1-1">2</a></sup> The <code>RankNTypes</code> GHC extension introduces a single exception: the <code>(-&gt;)</code> type constructor is special and may be applied to polymorphic types. That’s why <code>liftBaseWith</code> is legal, but <code>askRunInBase</code> is not: since <code>liftBaseWith</code> is passed a higher-order function that receives <code>runInBase</code> as an argument, the polymorphic type appears immediately under an application of <code>(-&gt;)</code>, which is allowed.</p><p>The aforementioned restriction means we’re basically out of luck, but if you <em>really</em> want <code>askRunInBase</code>, there is a workaround. GHC is perfectly alright with a field of a datatype being polymorphic, so we can define a newtype that wraps a suitably-polymorphic function:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">))</span></code></pre><p>We can now alter <code>askRunInBase</code> to return our newtype, and we can implement it in terms of <code>liftBaseWith</code>:<sup><a href="#footnote-2" id="footnote-ref-2-1">3</a></sup></p><pre><code class="pygments"><span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="nf">askRunInBase</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">runInBase</span></code></pre><p>To use <code>askRunInBase</code>, we have to pattern match on the <code>RunInBase</code> constructor, but it isn’t very noisy, since we can do it directly in a <code>do</code> binding. For example, we could implement a lifted version of <code>baz</code> this way:</p><pre><code class="pygments"><span class="nf">baz&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="nf">baz&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kt">RunInBase</span><span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">askRunInBase</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">))</span> +<span class="w"> </span><span class="n">bitraverse</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>As of version 1.0.2.3, <code>monad-control</code> does not provide a newtype like <code>RunInBase</code>, so it also doesn’t provide a function like <code>askRunInBase</code>. For now, you’ll have to use <code>liftBaseWith</code>, but it might be a useful future addition to the library.</p><h2><a name="pitfalls"></a>Pitfalls</h2><p>At this point in the blog post, we’ve covered the essentials of <code>MonadBaseControl</code>: how it works, how it’s designed, and how you might go about using it. However, so far, we’ve only considered situations where <code>MonadBaseControl</code> works well, and I’ve intentionally avoided examples where the technique breaks down. In this section, we’re going to take a look at the pitfalls and drawbacks of <code>MonadBaseControl</code>, plus some ways they can be mitigated.</p><h3><a name="no-polymorphism-no-lifting"></a>No polymorphism, no lifting</h3><p>All of the pitfalls of <code>MonadBaseControl</code> stem from the same root problem, and that’s the particular technique it uses to save and restore monadic state. We’ll start by considering one of the simplest ways that technique is thwarted, and that’s monomorphism. Consider the following two functions:</p><pre><code class="pygments"><span class="nf">poly</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span> +<span class="nf">mono</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">X</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">X</span></code></pre><p>Even after all we’ve covered, it may surprise you to learn that although <code>poly</code> can be easily lifted to <code>MonadBaseControl IO m =&gt; m a -&gt; m a</code>, it’s <em>impossible</em> to lift <code>mono</code> to <code>MonadBaseControl IO m =&gt; m X -&gt; m X</code>. It’s a little unintuitive, as we often think of polymorphic types as being more complicated (so surely lifting polymorphic functions ought to be harder), but in fact, it’s the flexibility of polymorphism that allows <code>MonadBaseControl</code> to work in the first place.</p><p>To understand the problem, remember that when we lift a function of type <code>forall a. b a -&gt; b a</code> using <code>MonadBaseControl</code>, we actually instantiate <code>a</code> to <code>(StM m c)</code>. That produces a function of type <code>b (StM m c) -&gt; b (StM m c)</code>, which is isomorphic to the <code>m c -&gt; m c</code> type we want. The instantiation step is easily overlooked, but it’s crucial, since otherwise we have no way to thread the state through the otherwise opaque function we’re trying to lift!</p><p>In the case of <code>mono</code>, that’s exactly the problem we’re faced with. <code>mono</code> will not accept an <code>IO (StM m X)</code> as an argument, only precisely an <code>IO X</code>, so we can’t pass along the monadic state. For all its machinery, <code>MonadBaseControl</code> is no help at all if no polymorphism is involved. Trying to generalize <code>mono</code> without modifying its implementation is a lost cause.</p><h3><a name="the-dangers-of-discarded-state"></a>The dangers of discarded state</h3><p>Our inability to lift <code>mono</code> is frustrating, but at least it’s conclusively impossible. In practice, however, many functions lie in an insidious in-between: polymorphic enough to be lifted, but not without compromises. The simplest of these functions have types such as the following:</p><pre><code class="pygments"><span class="nf">sideEffect</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Unlike <code>mono</code>, it’s entirely possible to lift <code>sideEffect</code>:</p><pre><code class="pygments"><span class="nf">sideEffect&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">sideEffect&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">sideEffect</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>This definition typechecks, but you may very well prefer it didn’t, since it has a serious problem: any changes made by <code>m</code> to the monadic state are completely discarded once <code>sideEffect'</code> returns! Since <code>sideEffect'</code> never calls <code>restoreM</code>, there’s no way the state of <code>m</code> can be any different from the original state, but it’s impossible to call <code>restoreM</code> since we don’t actually get an <code>StM m ()</code> result from <code>sideEffect</code>.</p><p>Sometimes this may be acceptable, since some monad transformers don’t actually have any output state anyway, such as <code>ReaderT r</code>. In other cases, however, <code>sideEffect'</code> could be a bug waiting to happen. One way to make <code>sideEffect'</code> safe would be to add a <code>StM m a ~ a</code> constraint to its context, since that guarantees the monad transformers being lifted through are stateless, and nothing is actually being discarded. Of course, that significantly restricts the set of monad transformers that can be lifted through.</p><h4><a name="rewindable-state"></a>Rewindable state</h4><p>One scenario where state discarding can actually be useful is operations with so-called rewindable or transactional state. The most common example of such an operation is <code>catch</code>:</p><pre><code class="pygments"><span class="nf">catch</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Exception</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>When lifted, state changes from the action <em>or</em> from the exception handler will be “committed,” but never both. If an exception is raised during the computation, those state changes are discarded (“rewound”), giving <code>catch</code> a kind of backtracking semantics. This behavior arises naturally from the way a lifted version of <code>catch</code> must be implemented:</p><pre><code class="pygments"><span class="nf">catch&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Exception</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">catch&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">catch</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>If <code>m</code> raises an exception, it will never return an <code>StM m a</code> value, so there’s no way to get ahold of any of the state changes that happened before the exception. Therefore, the only option is to discard that state.</p><p>This behavior is actually quite useful, and it’s definitely not unreasonable. However, useful or not, it’s inconsistent with state changes to mutable values like <code>IORef</code>s or <code>MVar</code>s (they stay modified whether an exception is raised or not), so it can still be a gotcha. Either way, it’s worth being aware of.</p><h4><a name="partially-discarded-state"></a>Partially discarded state</h4><p>The next function we’re going to examine is <code>finally</code>:</p><pre><code class="pygments"><span class="nf">finally</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function has a similar type to <code>catch</code>, and it even has similar semantics. Like <code>catch</code>, <code>finally</code> can be lifted, but unlike <code>catch</code>, its state <em>can’t</em> be given any satisfying treatment. The only way to implement a lifted version is</p><pre><code class="pygments"><span class="nf">finally&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">finally&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">finally</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">ma</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="n">mb</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span></code></pre><p>which always discards all state changes made by the second argument. This is clear just from looking at <code>finally</code>’s type: since <code>b</code> doesn’t appear anywhere in the return type, there’s simply no way to access that action’s result, and therefore no way to access its modified state.</p><p>However, don’t despair: there actually <em>is</em> a way to produce a lifted version of <code>finally</code> that preserves all state changes. It can’t be done by lifting <code>finally</code> directly, but if we reimplement <code>finally</code> in terms of simpler lifted functions that are more amenable to lifting, we can produce a lifted version of <code>finally</code> that preserves all the state:<sup><a href="#footnote-3" id="footnote-ref-3-1">4</a></sup></p><pre><code class="pygments"><span class="nf">finally&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">finally&#39;</span><span class="w"> </span><span class="n">ma</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mask&#39;</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">restore</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">try</span><span class="w"> </span><span class="p">(</span><span class="n">runInBase</span><span class="w"> </span><span class="p">(</span><span class="n">restore</span><span class="w"> </span><span class="n">ma</span><span class="p">))</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">mb</span><span class="w"> </span><span class="o">*&gt;</span><span class="w"> </span><span class="n">liftBase</span><span class="w"> </span><span class="p">(</span><span class="n">throwIO</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SomeException</span><span class="p">))</span> +<span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">restoreM</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">&lt;*</span><span class="w"> </span><span class="n">mb</span></code></pre><p>This illustrates an important (and interesting) point about <code>MonadBaseControl</code>: whether or not an operation can be made state-preserving is not a fundamental property of the operation’s type, but rather a property of the types of the exposed primitives. There is sometimes a way to implement a state-preserving variant of operations that might otherwise seem unliftable given the right primitives and a bit of cleverness.</p><h4><a name="forking-state"></a>Forking state</h4><p>As a final example, I want to provide an example where the state may not actually be discarded <em>per se</em>, just inaccessible. Consider the type of <code>forkIO</code>:</p><pre><code class="pygments"><span class="nf">forkIO</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">ThreadId</span></code></pre><p>Although <code>forkIO</code> isn’t actually polymorphic in its argument, we can convert <em>any</em> <code>IO</code> action to one that produces <code>()</code> via <code>void</code>, so it might as well be. Therefore, we can lift <code>forkIO</code> in much the same way we did with <code>sideEffect</code>:</p><pre><code class="pygments"><span class="nf">forkIO&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">ThreadId</span> +<span class="nf">forkIO&#39;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftBaseWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">runInBase</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">forkIO</span><span class="w"> </span><span class="p">(</span><span class="n">void</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">runInBase</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>As with <code>sideEffect</code>, we can’t recover the output state, but in this case, there’s a fundamental reason that goes deeper than the types: we’ve forked off a concurrent computation! We’ve therefore split the state in two, which might be what we want… but it also might not. <code>forkIO</code> is yet another illustration that it’s important to think about the state-preservation semantics when using <code>MonadBaseControl</code>, or you may end up with a bug!</p><h2><a name="monadbasecontrol-in-context"></a><code>MonadBaseControl</code> in context</h2><p>Congratulations: you’ve made it through most of this blog post. If you’ve followed everything so far, you now understand <code>MonadBaseControl</code>. All the tricky parts are over. However, before wrapping up, I’d like to add a little extra information about how <code>MonadBaseControl</code> relates to various other parts of the Haskell ecosystem. In practice, that information can be as important as understanding <code>MonadBaseControl</code> itself.</p><h3><a name="the-remainder-of-monad-control"></a>The remainder of <code>monad-control</code></h3><p>If you look at <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html">the documentation for <code>monad-control</code></a>, you’ll find that it provides more than just the <code>MonadBaseControl</code> typeclass. I’m not going to cover everything else in detail in this blog post, but I do want to touch upon it briefly.</p><p>First off, you should definitely take a look at the handful of helper functions provided by <code>monad-control</code>, such as <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:control"><code>control</code></a> and <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:liftBaseOp_"><code>liftBaseOp_</code></a>. These functions provide support for lifting common function types without having to use <code>liftBaseWith</code> directly. It’s useful to understand <code>liftBaseWith</code>, since it’s the most general way to use <code>MonadBaseControl</code>, but in practice, it is simpler and more readable to use the more specialized functions wherever possible. Many of the examples in this very blog post could be simplified using them, and I only stuck to <code>liftBaseWith</code> to introduce as few new concepts at a time as possible.</p><p>Second, I’d like to mention the related <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadTransControl"><code>MonadTransControl</code></a> typeclass. You hopefully remember from earlier in the blog post how we defined <code>MonadBaseControl</code> instances inductively so that we could lift all the way down to the base monad. <code>MonadTransControl</code> is like <code>MonadBaseControl</code> if it intentionally did <em>not</em> do that—it allows lifting through a single transformer at a time, rather than through all of them at once.</p><p>Usually, <code>MonadTransControl</code> is not terribly useful to use directly (though I did use it once <a href="/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/#making-mtls-classes-derivable">in a previous blog post of mine</a> to help derive instances of mtl-style classes), but it <em>is</em> useful for implementing <code>MonadBaseControl</code> instances for your own transformers. If you define a <code>MonadTransControl</code> instance for your monad transformer, you can get a <code>MonadBaseControl</code> implementation for free using the provided <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:ComposeSt"><code>ComposeSt</code></a>, <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultLiftBaseWith"><code>defaultLiftBaseWith</code></a>, and <a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#v:defaultRestoreM"><code>defaultRestoreM</code></a> bindings; see the documentation for more details.</p><h3><a name="lifted-base-and-lifted-async"></a><code>lifted-base</code> and <code>lifted-async</code></h3><p>If you’re going to use <code>MonadBaseControl</code>, the <a href="http://hackage.haskell.org/package/lifted-base"><code>lifted-base</code></a> and <a href="http://hackage.haskell.org/package/lifted-async"><code>lifted-async</code></a> packages are good to know about. As their names imply, they provide lifted versions of bindings in the <code>base</code> and <code>async</code> packages, so you can use them directly without needing to lift them yourself. For example, if you needed a lifted version of <code>mask</code> from <code>Control.Exception</code>, you could swap it for the <code>mask</code> export from <code>Control.Exception.Lifted</code>, and everything would mostly just work (though always be sure to check the documentation for any caveats on state discarding).</p><h3><a name="relationship-to-monadunliftio"></a>Relationship to <code>MonadUnliftIO</code></h3><p>Recently, FP Complete has developed the <a href="https://hackage.haskell.org/package/unliftio"><code>unliftio</code></a> package as an alternative to <code>monad-control</code>. It provides the <a href="https://hackage.haskell.org/package/unliftio-core-0.1.2.0/docs/Control-Monad-IO-Unlift.html#t:MonadUnliftIO"><code>MonadUnliftIO</code></a> typeclass, which is similar in spirit to <code>MonadBaseControl</code>, but heavily restricted: it is specialized to <code>IO</code> as the base monad, and it <em>only</em> allows instances for stateless monads, such as <code>ReaderT</code>. This is designed to encourage the so-called <a href="https://www.fpcomplete.com/blog/2017/06/readert-design-pattern"><code>ReaderT</code> design pattern</a>, which avoids ever using stateful monads like <code>ExceptT</code> or <code>StateT</code> over <code>IO</code>, encouraging the use of <code>IO</code> exceptions and mutable variables (e.g. <code>MVar</code>s or <code>TVar</code>s) instead.</p><p>I should be clear: I really like most of what FP Complete has done—to this day, I still use <code>stack</code> as my Haskell build tool of choice—and I think the suggestions given in the aforementioned “<code>ReaderT</code> design pattern” blog post have real weight to them. I have a deep respect for Michael Snoyman’s commitment to opinionated, user-friendly tools and libraries. But truthfully, I can’t stand <code>MonadUnliftIO</code>.</p><p><code>MonadUnliftIO</code> is designed to avoid all the complexity around state discarding that <code>MonadBaseControl</code> introduces, and on its own, that’s a noble goal. Safety first, after all. The problem is that <code>MonadUnliftIO</code> really is extremely limiting, and what’s more, it can actually be trivially encoded in terms of <code>MonadBaseControl</code> as follows:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">MonadUnliftIO</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">StM</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This alias can be used to define safe, lifted functions that never discard state while still allowing functions that <em>can</em> be safely lifted through stateful transformers to do so. Indeed, the <a href="https://hackage.haskell.org/package/lifted-async-0.10.0.4/docs/Control-Concurrent-Async-Lifted-Safe.html"><code>Control.Concurrent.Async.Lifted.Safe</code></a> module from <code>lifted-async</code> does exactly that (albeit with a slightly different formulation than the above alias).</p><p>To be fair, the <code>unliftio</code> README does address this in its <a href="https://github.com/fpco/unliftio/tree/bb2e26e7fbbaebb15555f417ba9753a76b3218b2/unliftio#monad-control">comparison section</a>:</p><blockquote><p><code>monad-control</code> allows us to unlift both styles. In theory, we could write a variant of <code>lifted-base</code> that never does state discards […] In other words, this is an advantage of <code>monad-control</code> over <code>MonadUnliftIO</code>. We've avoided providing any such extra typeclass in this package though, for two reasons:</p><ul><li><p><code>MonadUnliftIO</code> is a simple typeclass, easy to explain. We don't want to complicated [sic] matters […]</p></li><li><p>Having this kind of split would be confusing in user code, when suddenly [certain operations are] not available to us.</p></li></ul></blockquote><p>In other words, the authors of <code>unliftio</code> felt that <code>MonadBaseControl</code> was simply not worth the complexity, and they could get away with <code>MonadUnliftIO</code>. Frankly, if you feel the same way, by all means, use <code>unliftio</code>. I just found it too limiting given the way I write Haskell, plain and simple.</p><h2><a name="recap"></a>Recap</h2><p>So ends another long blog post. As often seems the case, I set out to write something short, but I ended up writing well over 5,000 words. I suppose that means I learned something from this experience, too: <code>MonadBaseControl</code> is more complicated than I had anticipated! Maybe there’s something to take away from that.</p><p>In any case, it’s over now, so I’d like to briefly summarize what we’ve covered:</p><ul><li><p><a href="https://hackage.haskell.org/package/monad-control-1.0.2.3/docs/Control-Monad-Trans-Control.html#t:MonadBaseControl"><code>MonadBaseControl</code></a> allows us to lift higher-order monadic operations.</p></li><li><p>It operates by capturing the current monadic state and explicitly threading it through the action in the base monad before restoring it.</p></li><li><p>That technique works well for polymorphic operations for the type <code>forall a. b a -&gt; b a</code>, but it can be tricky or even impossible for more complex operations, sometimes leading to discarded state.</p><p>This can sometimes be mitigated by restricting certain operations to stateless monads using a <code>StM m a ~ a</code> constraint, or by reimplementing the operation in terms of simpler primitives.</p></li><li><p>The <a href="http://hackage.haskell.org/package/lifted-base"><code>lifted-base</code></a> and <a href="http://hackage.haskell.org/package/lifted-async"><code>lifted-async</code></a> packages provide lifted versions of existing operations, avoiding the need to lift them yourself.</p></li></ul><p>As with many abstractions in Haskell, don’t worry too much if you don’t have a completely firm grasp of <code>MonadBaseControl</code> at first. Insight often comes with repeated experience, and <code>monad-control</code> can still be used in useful ways even without a perfect understanding. My hope is that this blog post has helped you build intuitions about <code>MonadBaseControl</code> even if some of the underlying machinery remains a little fuzzy, and I hope it can also serve as a reference for those who want or need to understand (or just be reminded of) all the little details.</p><p>Finally, I’ll admit <code>MonadBaseControl</code> isn’t especially elegant or beautiful as Haskell abstractions go. In fact, in many ways, it’s a bit of a kludge! Perhaps, in time, effect systems will evolve and mature so that it and its ilk are no longer necessary, and they may become distant relics of an inferior past. But in the meantime, it’s here, it’s useful, and I think it’s worth embracing. If you’ve shied away from it in the past, I hope I’ve illuminated it enough to make you consider giving it another try.</p><ol class="footnotes"><li id="footnote-0"><p>One example of a function with that type is <code>mask_</code>. <a href="#footnote-ref-0-1">↩</a></p></li><li id="footnote-1"><p>Types with polymorphic types under type constructors are called <em>impredicative</em>. GHC technically has limited support for impredicativity via the <code>ImpredicativeTypes</code> language extension, but as of GHC 8.8, it has been fairly broken for some time. A fix is apparently being worked on, but even if that effort is successful, I don’t know what impact it will have on type inference. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Note that <code>askRunInBase = liftBaseWith (pure . RunInBase)</code> does <em>not</em> typecheck, as it would require impredicative polymorphism: it would require instantiating the type of <code>(.)</code> with polymorphic types. The version using <code>($)</code> works because GHC actually has special typechecking rules for <code>($)</code>! Effectively, <code>f $ x</code> is really syntax in GHC. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Assume that <code>mask'</code> is a suitably lifted version of <code>mask</code> (which can in fact be made state-preserving). <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>An opinionated guide to Haskell in 2018https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/10 Feb 2018<article><p>For me, this month marks the end of an era in my life: as of February 2018, I am no longer employed writing Haskell. It’s been a fascinating two years, and while I am excitedly looking forward to what I’ll be doing next, it’s likely I will continue to write Haskell in my spare time. I’ll probably even write it again professionally in the future.</p><p>In the meantime, in the interest of both sharing with others the small amount of wisdom I’ve gained and preserving it for my future self, I’ve decided to write a long, rather dry overview of a few select parts of the Haskell workflow I developed and the ecosystem I settled into. This guide is, as the title notes, <em>opinionated</em>—it is what I used in my day-to-day work, nothing more—and I don’t claim that anything here is the only way to write Haskell, nor even the best way. It is merely what I found helpful and productive. Take from it as much or as little as you’d like.</p><h2><a name="build-tools-and-how-to-use-them"></a>Build tools and how to use them</h2><p>When it comes to building Haskell, you have options. And frankly, most of them are pretty good. There was a time when <code>cabal-install</code> had a (warranted) reputation for being nearly impossible to use and regularly creating dependency hell, but I don’t think that’s the case anymore (though you <em>do</em> need to be a little careful about how you use it). Sandboxed builds work alright, and <code>cabal new-build</code> and the other <code>cabal new-*</code> commands are even better. That said, the UX of <code>cabal-install</code> is still less-than-stellar, and it has sharp edges, especially for someone coming from an ecosystem without a heavyweight compilation process like JavaScript, Ruby, or Python.</p><p>Nix is an alternative way to manage Haskell dependencies, and it seems pretty cool. It has a reputation for being large and complicated, and that reputation does not seem especially unfair, but you get lots of benefits if you’re willing to pay the cost. Unfortunately, I have never used it (though I’ve read a lot about it), so I can’t comment much on it here. Perhaps I’ll try to go all-in with Nix when I purchase my next computer, but for now, my workflow works well enough that I don’t feel compelled to switch.</p><p>Personally, I use <code>stack</code> as my Haskell build tool. It’s easy to use, it works out of the box, and while it doesn’t enjoy the same amount of caching as <code>cabal new-build</code> or Nix, it caches most packages, and it also makes things like git-hosted sources incredibly easy, which (as far as I can tell) can’t be done with <code>cabal-install</code> alone.</p><p>This section is going to be a guide on how <em>I</em> use <code>stack</code>. If you use <code>cabal-install</code> with or without Nix, great! Those tools seem good, too. This is not an endorsement of <code>stack</code> over the other build tools, just a description of how I use it, the issues I ran into, and my solutions to them.</p><h3><a name="understanding-stack-s-model-and-avoiding-its-biggest-gotcha"></a>Understanding <code>stack</code>’s model and avoiding its biggest gotcha</h3><p>Before using <code>stack</code>, there are a few things every programmer should know:</p><ul><li><p><code>stack</code> is not a package manager, it is a build tool. It does not manage a set of “installed” packages; it simply builds targets and their dependencies.</p></li><li><p>The command to build a target is <code>stack build &lt;target&gt;</code>. Just using <code>stack build</code> on its own will build the current project’s targets.</p></li><li><p><strong>You almost certainly do not want to use <code>stack install</code>.</strong></p></li></ul><p>This is the biggest point of confusion I see among new users of <code>stack</code>. After all, when you want to install a package with <code>npm</code>, you type <code>npm install &lt;package&gt;</code>. So a new Haskeller decides to install <code>lens</code>, types <code>stack install lens</code>, and then later tries <code>stack uninstall lens</code>, only to discover that no such command exists. What happened?</p><p><code>stack install</code> is not like <code>npm install</code>. <code>stack install</code> is like <code>make install</code>. It is nothing more than an alias for <code>stack build --copy-bins</code>, and <em>all</em> it does is build the target and copy all of its executables into some relatively global location like <code>~/.local/bin</code>. This is usually not what you want.</p><p>This design decision is not unique to <code>stack</code>; <code>cabal-install</code> suffers from it as well. One can argue that it isn’t unintuitive because it really is just following what <code>make install</code> conventionally does, and the fact that it happens to conflict with things like <code>npm install</code> or even <code>apt-get install</code> is just a naming clash. I think that argument is a poor one, however, and I think the decision to even include a <code>stack install</code> command was a bad idea.</p><p>So, remember: don’t use <code>stack install</code>! <code>stack</code> works best when everything lives inside the current project’s <em>local</em> sandbox, and <code>stack install</code> copies executables into a <em>global</em> location by design. While it might sometimes appear to work, it’s almost always wrong. The <em>only</em> situation in which <code>stack install</code> is the right answer is when you want to install an executable for a use unrelated to Haskell development (that is, something like <code>pandoc</code>) that just so happens to be provided by a Haskell package. <strong>This means no running <code>stack install ghc-mod</code> or <code>stack install intero</code> either, no matter what READMEs might tell you!</strong> Don’t worry: I’ll cover the proper way to install those things later.</p><h3><a name="actually-building-your-project-with-stack"></a>Actually building your project with <code>stack</code></h3><p>Okay, so now that you know to never use <code>stack install</code>, what <em>do</em> you use? Well, <code>stack build</code> is probably all you need. Let’s cover some variations of <code>stack build</code> that I use most frequently.</p><p>Once you have a <code>stack</code> project, you can build it by simply running <code>stack build</code> within the project directory. However, for local development, this is usually unnecessarily slow because it runs the GHC optimizer. For faster development build times, pass the <code>--fast</code> flag to disable optimizations:</p><pre><code>$ stack build --fast +</code></pre><p>By default, <code>stack</code> builds dependencies with coarse-grained, package-level parallelism, but you can enable more fine-grained, module-level parallel builds by adding <code>--ghc-options=-j</code>. Unfortunately, there are conflicting accounts on whether or not this actually makes things faster or slower in practice, and I haven’t extensively tested to see whether or not this is the case, so I mostly leave it off.</p><p>Usually, you also want to build and run the tests along with your code, which you can enable with the <code>--test</code> flag. Additionally, <code>stack test</code> is an alias for <code>stack build --test</code>, so these two commands are equivalent:</p><pre><code>$ stack build --fast --test +$ stack test --fast +</code></pre><p>Also, it is useful to build documentation as well as code! You can do this by passing the <code>--haddock</code> flag, but unfortunately, I find Haddock sometimes takes an unreasonably long time to run. Therefore, since I usually only care about running Haddock on my dependencies, I usually pass the <code>--haddock-deps</code> flag instead, which prevents having to re-run Haddock every time you build:</p><pre><code>$ stack test --fast --haddock-deps +</code></pre><p>Finally, I usually want to build and test my project in the background whenever my code changes. Fortunately, this can be done easily by using the <code>--file-watch</code> flag, making it easy to incrementally change project code and immediately see results:</p><pre><code>$ stack test --fast --haddock-deps --file-watch +</code></pre><p>This is the command I usually use to develop my Haskell projects.</p><h3><a name="accessing-local-documentation"></a>Accessing local documentation</h3><p>While Haskell does not always excel on the documentation front, a small amount of documentation is almost always better than no documentation at all, and I find my dependencies’ documentation to be an invaluable resource while developing. I find many people just look at docs on Hackage or use the hosted instance of Hoogle, but this sometimes leads people astray: they might end up looking at the wrong version of the documentation! Fortunately, there’s an easy solution to this problem, which is to browse the documentation <code>stack</code> installs locally, which is guaranteed to match the version you are using in your current project.</p><p>The easiest way to open local documentation for a particular package is to use the <code>stack haddock --open</code> command. For example, to open the documentation for <code>lens</code>, you could use the following command:</p><pre><code>$ stack haddock --open lens +</code></pre><p>This will open the local documentation in your web browser, and you can browse it at your leisure. If you have already built the documentation using the <code>--haddock-deps</code> option I recommended in the previous section, this command should complete almost instantly, but if you haven’t built the documentation yet, you’ll have to wait as <code>stack</code> builds it for you on-demand.</p><p>While this is a good start, it isn’t perfect. Ideally, I want to have <em>searchable</em> documentation, and fortunately, this is possible to do by running Hoogle locally. This is easy enough with modern versions of <code>stack</code>, which have built-in Hoogle integration, but it still requires a little bit of per-project setup, since you need to build the Hoogle search index with the following command:</p><pre><code>$ stack hoogle -- generate --local +</code></pre><p>This will install Hoogle into the current project if it isn’t already installed, and it will index your dependencies’ documentation and generate a new Hoogle database. Once you’ve done that, you can start a web server that serves a local Hoogle search page with the following command:</p><pre><code>$ stack hoogle -- server --local --port=8080 +</code></pre><p>Navigate to <code>http://localhost:8080</code> in your web browser, and you’ll have a fully-searchable index of all your Haskell packages’ documentation. Isn’t that neat?</p><p>Unfortunately, you <em>will</em> have to manually regenerate the Hoogle database when you install new packages and their documentation, which you can do by re-running <code>stack hoogle -- generate --local</code>. Fortunately, regenerating the database doesn’t take very long, as long as you’ve been properly rebuilding the documentation with <code>--haddock-deps</code>.</p><h3><a name="configuring-your-project"></a>Configuring your project</h3><p>Every project built with <code>stack</code> is configured with two separate files:</p><ul><li><p>The <code>stack.yaml</code> file, which controls which packages are built and what versions to pin your dependencies to.</p></li><li><p>The <code>&lt;project&gt;.cabal</code> file <em>or</em> <code>package.yaml</code> file, which specifies build targets, their dependencies, and which GHC options to apply, among other things.</p></li></ul><p>The <code>.cabal</code> file is, ultimately, what is used to build your project, but modern versions of <code>stack</code> generate projects that use hpack, which uses an alternate configuration file, the <code>package.yaml</code> file, to generate the <code>.cabal</code> file. This can get a little bit confusing, since it means you have <em>three</em> configuration files in your project, one of which is generated from the other one.</p><p>I happen to use and like hpack, so I use a <code>package.yaml</code> file and allow hpack to generate the <code>.cabal</code> file. I have no real love for YAML, and in fact I think custom configuration formats are completely fine, but the primary advantage of hpack is the ability to specify things like GHC options and default language extensions for all targets at once, instead of needing to duplicate them per-target.</p><p>You can think of the <code>.cabal</code> or <code>package.yaml</code> file as a specification for <em>how</em> your project is built and <em>what packages</em> it depends on, but the <code>stack.yaml</code> file is a specification of precisely <em>which version</em> of each package should be used and where it should be fetched from. Also, each <code>.cabal</code> file corresponds to precisely <em>one</em> Haskell package (though it may have any number of executable targets), but a <code>stack.yaml</code> file can specify multiple different packages to build, useful for multi-project builds that share a common library. The details here can be a little confusing, more than I am likely going to be able to explain in this blog post, but for the most part, you can get away with the defaults unless you’re doing something fancy.</p><h3><a name="setting-up-editor-integration"></a>Setting up editor integration</h3><p>Currently, I use Atom to write Haskell. Atom is not a perfect editor by any means, and it leaves a lot to be desired, but it’s easy to set up, and the Haskell editor integration is decent.</p><p>Atom’s editor integration is powered by <code>ghc-mod</code>, a program that uses the GHC API to provide tools to inspect Haskell programs. Installing <code>ghc-mod</code> must be done manually so that Atom’s <code>haskell-ghc-mod</code> package can find it, and this is where a lot of people get tripped up. They run <code>stack install ghc-mod</code>, it installs <code>ghc-mod</code> into <code>~/.local/bin</code>, they put that in their <code>PATH</code>, and things work! …except when a new version of GHC is released a few months later, everything stops working.</p><p>As mentioned above, <strong><code>stack install</code> is not what you want</strong>. Tools like <code>ghc-mod</code>, <code>hlint</code>, <code>hoogle</code>, <code>weeder</code>, and <code>intero</code> work best when installed as part of the sandbox, <em>not</em> globally, since that ensures they will match the current GHC version your project is using. This can be done per-project using the ordinary <code>stack build</code> command, so the easiest way to properly install <code>ghc-mod</code> into a <code>stack</code> project is with the following command:</p><pre><code>$ stack build ghc-mod +</code></pre><p>Unfortunately, this means you will need to run that command inside every single <code>stack</code> project individually in order to properly set it up so that <code>stack exec -- ghc-mod</code> will find the correct executable. One way to circumvent this is by using a recently-added <code>stack</code> flag designed for this explicit purpose, <code>--copy-compiler-tool</code>. This is like <code>--copy-bins</code>, but it copies the executables into a <em>compiler-specific location</em>, so a tool built for GHC 8.0.2 will be stored separately from the same tool built for GHC 8.2.2. <code>stack exec</code> arranges for the executables for the current compiler version to end up in the <code>PATH</code>, so you only need to build and install your tools once per compiler version.</p><p>Does this kind of suck? Yes, a little bit, but it sucks a whole lot less than all your editor integration breaking every time you switch to a project that uses a different version of GHC. I use the following command in a fresh sandbox when a Stackage LTS comes out for a new version of GHC:</p><pre><code>$ stack build --copy-compiler-tool ghc-mod hoogle weeder +</code></pre><p>This way, I only have to build those tools once, and I don’t worry about rebuilding them again until a the next release of GHC. To verify that things are working properly, you should be able to create a fresh <code>stack</code> project, run a command like this one, and get a similar result:</p><pre><code>$ stack exec -- which ghc-mod +/Users/alexis/.stack/compiler-tools/x86_64-osx/ghc-8.2.2/bin/ghc-mod +</code></pre><p>Note that this path is scoped to my operating system and my compiler version, but nothing else—no LTS or anything like that.</p><h2><a name="warning-flags-for-a-safe-build"></a>Warning flags for a safe build</h2><p>Haskell is a relatively strict language as programming languages go, but in my experience, it isn’t quite strict enough. Many things are not errors that probably ought to be, like orphan instances and inexhaustive pattern matches. Fortunately, GHC provides <em>warnings</em> that catch these problems statically, which fill in the gaps. I recommend using the following flags on all projects to ensure everything is caught:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat"><code>-Wcompat</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates"><code>-Wincomplete-record-updates</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints"><code>-Wredundant-constraints</code></a></p></li></ul><p>The <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> option turns on <em>most</em> warnings, but (ironically) not all of them. The <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Weverything"><code>-Weverything</code></a> flag truly turns on <em>all</em> warnings, but some of the warnings left disabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> really are quite silly, like warning when type signatures on polymorphic local bindings are omitted. Some of them, however, are legitimately useful, so I recommend turning them on explicitly.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wcompat"><code>-Wcompat</code></a> enables warnings that make your code more robust in the face of future backwards-incompatible changes. These warnings are trivial to fix and serve as free future-proofing, so I see no reason not to turn these warnings on.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-record-updates"><code>-Wincomplete-record-updates</code></a> and <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a> are things I think ought to be enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> because they both catch what are essentially partial pattern-matches (and therefore runtime errors waiting to happen). The fact that <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-uni-patterns"><code>-Wincomplete-uni-patterns</code></a> <em>isn’t</em> enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a> is so surprising that it can lead to bugs being overlooked, since the extremely similar <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wincomplete-patterns"><code>-Wincomplete-patterns</code></a> <em>is</em> enabled by <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wall"><code>-Wall</code></a>.</p><p><a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wredundant-constraints"><code>-Wredundant-constraints</code></a> is a useful warning that helps to eliminate unnecessary typeclass constraints on functions, which can sometimes occur if a constraint was previously necessary but ends up becoming redundant due to a change in the function’s behavior.</p><p>I put all five of these flags in the <code>.cabal</code> file (or <code>package.yaml</code>), which enables them everywhere, but this alone is unlikely to enforce a warning-free codebase, since the build will still succeed even in the presence of warnings. Therefore, when building projects in CI, I pass the <a href="https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Werror"><code>-Werror</code></a> flag (using <code>--ghc-options=-Werror</code> for <code>stack</code>), which treats warnings as errors and halts the build if any warnings are found. This is useful, since it means warnings don’t halt the whole build while developing, making it possible to write some code that has warnings and still run the test suite, but it still enforces that pushed code be warning-free.</p><h2><a name="any-flavor-you-like"></a>Any flavor you like</h2><p>Haskell is both a language and a spectrum of languages. It is both a standard and a specific implementation. Haskell 98 and Haskell 2010 are good, small languages, and there are a few different implementations, but when people talk about “Haskell”, unqualified, they’re almost always talking about GHC.</p><p>GHC Haskell, in stark contrast to standard Haskell, is neither small nor particularly specific, since GHC ships with <em>dozens</em> of knobs and switches that can be used to configure the language. In theory, this is a little terrifying. How could anyone ever hope to talk about Haskell and agree upon how to write it if there are so many <em>different</em> Haskells, each a little bit distinct? Having a cohesive ecosystem would be completely hopeless.</p><p>Fortunately, in practice, this is not nearly as bad as it seems. The majority of GHC extensions are simple switches: a feature is either on or it is off. Turning a feature on rarely affects code that does not use it, so most extensions can be turned on by default, and programmers may simply avoid the features they do not wish to use, just as any programmer in any programming language likely picks a subset of their language’s features to use on a daily basis. Writing Haskell is not different in this regard, only in the sense that it does not allow all features to be used by default; everything from minor syntactic tweaks to entirely new facets of the type system are opt-in.</p><p>Frankly, I think the UX around this is terrible. I recognize the desire to implement a standard Haskell, and the old <code>-fglasgow-exts</code> was not an especially elegant solution for people wishing to use nonstandard Haskell, but having to insert <code>LANGUAGE</code> pragmas at the top of every module just to take advantage of the best features GHC has to offer is a burden, and it is unnecessarily intimidating. I think much of the Haskell community finds the use of <code>LANGUAGE</code> pragmas preferable to enabling extensions globally using the <code>default-extensions</code> list in the <code>.cabal</code> file, but I cut across the grain on that issue <em>hard</em>. The vast majority of language extensions I use are extensions I want enabled all the time; a list of them at the top of a module is just distracting noise, and it only serves to bury the extensions I really do want to enable on a module-by-module basis. It also makes it tricky to communicate with a team which extensions are acceptable (or even preferable) and which are discouraged.</p><p>My <em><strong>strong</strong></em> recommendation if you decide to write GHC Haskell on a team is to agree as a group to a list of extensions the team is happy with enabling everywhere and putting those extensions in the <code>default-extensions</code> list in the <code>.cabal</code> file. This eliminates clutter, busywork, and the conceptual overhead of remembering which extensions are in favor, and which are discouraged. This is a net win, and it isn’t at all difficult to look in the <code>.cabal</code> file when you want to know which extensions are in use.</p><p>Now, with that small digression out of the way, the question becomes precisely which extensions should go into that <code>default-extensions</code> list. I happen to like using most of the features GHC makes available, so I enable a whopping <strong>34</strong> language extensions <em>by default</em>. As of GHC 8.2, here is my list:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable"><code>DeriveFoldable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor"><code>DeriveFunctor</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric"><code>DeriveGeneric</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift"><code>DeriveLift</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable"><code>DeriveTraversable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a></p></li></ul><p>This is a lot, and a few of them are likely to be more controversial than others. Since I do not imagine everyone will agree with everything in this list, I’ve broken it down into smaller chunks, arranged from what I think ought to be least controversial to most controversial, along with a little bit of justification why each extension is in each category. If you’re interested in coming up with your own list of extensions, the rest of this section is for you.</p><h3><a name="trivial-lifting-of-standards-imposed-limitations"></a>Trivial lifting of standards-imposed limitations</h3><p>A few extensions are tiny changes that lift limitations that really have no reason to exist, other than that they are mandated by the standard. I am not sure why these restrictions are in the standard to begin with, other than perhaps a misguided attempt at making the language simpler. These extensions include the following:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a></p></li></ul><p>These extensions have no business <em>not</em> being turned on everywhere. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a> end up being turned on in almost any nontrivial Haskell module, since without them, the typeclass system is pointlessly and artificially limited.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs"><code>InstanceSigs</code></a> is extremely useful, completely safe, and has zero downsides.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses"><code>MultiParamTypeClasses</code></a> are almost impossible to avoid, given how many libraries use them, and they are a completely obvious generalization of single-parameter typeclasses. Much like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts"><code>FlexibleContexts</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances"><code>FlexibleInstances</code></a>, I see no real reason to ever leave these disabled.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a> is even stranger to me, since <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyDataDecls"><code>EmptyDataDecls</code></a> is in Haskell 2010, so it’s possible to define empty datatypes in standard Haskell but not exhaustively pattern-match on them! This is silly, and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase"><code>EmptyCase</code></a> should be standard Haskell.</p><h3><a name="syntactic-conveniences"></a>Syntactic conveniences</h3><p>A few GHC extensions are little more than trivial, syntactic abbreviations. These things would be tiny macros in a Lisp, but they need to be extensions to the compiler in Haskell:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a></p></li></ul><p>All of these extensions are only triggered by explicit use of new syntax, so existing programs will never change behavior when these extensions are introduced.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a> only saves a few characters, but it eliminates the need to come up with a fresh, unique variable name that will only be used once, which is sometimes hard to do and leads to worse names overall. Sometimes, it really is better to leave something unnamed.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf"><code>MultiWayIf</code></a> isn’t something I find I commonly need, but when I do, it’s nice to have. It’s far easier to read than nested <code>if...then...else</code> chains, and it uses the existing guard syntax already used with function declarations and <code>case...of</code>, so it’s easy to understand, even to those unfamiliar with the extension.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns"><code>NamedFieldPuns</code></a> avoids headaches and clutter when using Haskell records without the <a href="https://www.reddit.com/r/haskell/comments/6jaa5f/recordwildcards_and_binary_parsing/djd5ugj/">accidental identifier capture issues</a> of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards"><code>RecordWildCards</code></a>. It’s a nice, safe compromise that brings some of the benefits of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards"><code>RecordWildCards</code></a> without any downsides.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections"><code>TupleSections</code></a> is a logical generalization of tuple syntax in the same vein as standard operator sections, and it’s quite useful when using applicative notation. I don’t see any reason to not enable it.</p><h3><a name="extensions-to-the-deriving-mechanism"></a>Extensions to the deriving mechanism</h3><p>GHC’s typeclass deriving mechanism is one of the things that makes Haskell so pleasant to write, and in fact I think Haskell would be nearly unpalatable to write without it. Boilerplate generation is a good thing, since it defines operations in terms of a single source of truth, and generated code is code you do not need to maintain. There is rarely any reason to write a typeclass instance by hand when the deriving mechanism will write it automatically.</p><p>These extensions give GHC’s typeclass deriving mechanism more power without any cost. Therefore, I see no reason <em>not</em> to enable them:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable"><code>DeriveFoldable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor"><code>DeriveFunctor</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric"><code>DeriveGeneric</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift"><code>DeriveLift</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable"><code>DeriveTraversable</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a></p></li></ul><p>The first five of these simply extend the list of typeclasses GHC knows how to derive, something that will only ever be triggered if the user explicitly requests GHC derive one of those classes. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a> is quite possibly one of the most important extensions in all of Haskell, since it dramatically improves <code>newtype</code>s’ utility. Wrapper types can inherit instances they need without any boilerplate, and making increased type safety easier and more accessible is always a good thing in my book.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a> is new to GHC 8.2, but it finally presents the functionality of GHC’s <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> extension in a useful way. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> is useful when used with certain libraries that use <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a> (discussed later) with <code>GHC.Generics</code> to derive instances of classes without the deriving being baked into GHC. Unfortunately, enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a> essentially disables the far more useful <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving"><code>GeneralizedNewtypeDeriving</code></a>, so I do <em>not</em> recommend enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass"><code>DeriveAnyClass</code></a>. Fortunately, with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDerivingStrategies"><code>DerivingStrategies</code></a>, it’s possible to opt into the <code>anyclass</code> deriving strategy on a case-by-case basis, getting some nice boilerplate reduction in the process.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving"><code>StandaloneDeriving</code></a> is useful when GHC’s deriving algorithms aren’t <em>quite</em> clever enough to deduce the instance context automatically, so it allows specifying it manually. This is only useful in a few small situations, but it’s nice to have, and there are no downsides to enabling it, so it ought to be turned on.</p><h3><a name="lightweight-syntactic-adjustments"></a>Lightweight syntactic adjustments</h3><p>A couple extensions tweak Haskell’s syntax in more substantial ways than things like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase"><code>LambdaCase</code></a>, but not in a significant enough way for them to really be at all surprising:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns"><code>BangPatterns</code></a> mirror strictness annotations on datatypes, so they are unlikely to be confusing, and they provide a much more pleasant notation for annotating the strictness of bindings than explicit uses of <code>seq</code>.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a> are also fairly self-explanatory: they’re just like type annotations, but for types instead of values. Writing kind signatures explicitly is usually unnecessary, but they can be helpful for clarity or for annotating phantom types when <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPolyKinds"><code>PolyKinds</code></a> is not enabled. Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures"><code>KindSignatures</code></a> doesn’t have any adverse effects, so I see no reason not to enable it everywhere.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators"><code>TypeOperators</code></a> adjusts the syntax of types slightly, allowing operators to be used as type constructors and written infix, which is technically backwards-incompatible, but I’m a little suspicious of anyone using <code>(!@#$)</code> as a type variable (especially since standard Haskell does not allow them to be written infix). This extension is useful with some libraries like <code>natural-transformations</code> that provide infix type constructors, and it makes the type language more consistent with the value language.</p><h3><a name="polymorphic-string-literals"></a>Polymorphic string literals</h3><p>I’m putting this extension in a category all of its own, mostly because I don’t think any other Haskell extensions have quite the same set of tradeoffs:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a></p></li></ul><p>For me, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is not optional. Haskell’s infamous “string problem” (discussed in more detail at the end of this blog post) means that <code>String</code> is a linked list of characters, and all code that cares about performance actually uses <code>Text</code>. Manually invoking <code>pack</code> on every single string literal in a program is just noise, and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> solves that noise.</p><p>That said, I actually find I don’t use the polymorphism of string literals very often, and I’d be alright with monomorphic literals if I could make them <em>all</em> have type <code>Text</code>. Unfortunately, there isn’t a way to do this, so <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is the next best thing, even if it sometimes causes some unnecessary ambiguities that require type annotations to resolve.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings"><code>OverloadedStrings</code></a> is an extension that I use so frequently, in so many modules (especially in my test suites) that I would rather keep it on everywhere so I don’t have to care about whether or not it’s enabled in the module I’m currently writing. On the other hand, it certainly isn’t my favorite language extension, either. I wouldn’t go as far as to call it a necessary evil, since I don’t think it’s truly “evil”, but it does seem to be necessary.</p><h3><a name="simple-extensions-to-aid-type-annotation"></a>Simple extensions to aid type annotation</h3><p>The following two extensions significantly round out Haskell’s language for referring to types, making it much easier to insert type annotations where necessary (for removing ambiguity or for debugging type errors):</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a></p></li></ul><p>That the behavior of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a> is <em>not</em> the default is actually one of the most common gotchas for new Haskellers. Sadly, it can theoretically adjust the behavior of existing Haskell programs, so I cannot include it in the list of trivial changes, but I would argue such programs were probably confusing to begin with, and I have never seen a program in practice that was impacted by that problem. I think leaving <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a> off is much, much more likely to be confusing than turning it on.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a> is largely unrelated, but I include it in this category because it’s quite useful and cooperates well with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables"><code>ScopedTypeVariables</code></a>. Use of <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeApplications"><code>TypeApplications</code></a> makes instantiation much more lightweight than full-blown type annotations, and once again, it has no downsides if it is enabled and unused (since it is a syntactic addition). I recommend enabling it.</p><h3><a name="simple-extensions-to-the-haskell-type-system"></a>Simple extensions to the Haskell type system</h3><p>A few extensions tweak the Haskell type system in ways that I think are simple enough to be self-explanatory, even to people who might not have known they existed. These are as follows:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds"><code>ConstraintKinds</code></a> is largely just used to define typeclass aliases, which is both useful and self-explanatory. Unifying the type and constraint language also has the effect of allowing type-level programming with constraints, which is sometimes useful, but far rarer in practice than the aforementioned use case.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes"><code>RankNTypes</code></a> are uncommon, looking at the average type in a Haskell program, but they’re certainly nice to have when you need them. The idea of pushing <code>forall</code>s further into a type to adjust how variables are quantified is something that I find people find fairly intuitive, especially after seeing them used once or twice, and higher-rank types do crop up regularly, if infrequently.</p><h3><a name="intermediate-syntactic-adjustments"></a>Intermediate syntactic adjustments</h3><p>Three syntactic extensions to Haskell are a little bit more advanced than the ones I’ve already covered, and none of them are especially related:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is, on the surface, simple. It changes <code>do</code> notation to use <code>Applicative</code> operations where possible, which allows using <code>do</code> notation with applicative functors that are not monads, and it also makes operations potentially more performant when <code>(&lt;*&gt;)</code> can be implemented more efficiently than <code>(&gt;&gt;=)</code>. In theory, it sounds like there are no downsides to enabling this everywhere. However, there are are a few drawbacks that lead me to put it so low on this list:</p><ol><li><p>It considerably complicates the desugaring of <code>do</code> blocks, to the point where the algorithm cannot even be easily syntactically documented. In fact, an additional compiler flag, <code>-foptimal-applicative-do</code>, is a way to <em>opt into</em> optimal solutions for <code>do</code> block expansions, tweaking the desugaring algorithm to have an <em>O</em>(<em>n</em><sup>3</sup>) time complexity! This means that the default behavior is guided by a heuristic, and desugaring isn’t even especially predictable. This isn’t necessarily so bad, since it’s really only intended as an optimization when some <code>Monad</code> operations are still necessary, but it does dramatically increase the complexity of one of Haskell’s core forms.</p></li><li><p>The desugaring, despite being <em>O</em>(<em>n</em><sup>2</sup>) by default, isn’t even especially clever. It relies on a rather disgusting hack that recognizes <code>return e</code>, <code>return $ e</code>, <code>pure e</code>, or <code>pure $ e</code> expressions <em>syntactically</em>, and it completely gives up if an expression with precisely that shape is not the final statement in a <code>do</code> block. This is a bit awkward, since it effectively turns <code>return</code> and <code>pure</code> into syntax when before they were merely functions, but that isn’t all. It also means that the following <code>do</code> block is <em>not</em> desugared using <code>Applicative</code> operations:</p><pre><code class="pygments"><span class="kr">do</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">t</span> +<span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="n">z</span></code></pre><p>This will use the normal, monadic desugaring, despite the fact that it is trivially desugared into <code>Applicative</code> operations as <code>foo a b *&gt; bar s t *&gt; baz y z</code>. In order to get <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> to trigger here, the <code>do</code> block must be contorted into the following:</p><pre><code class="pygments"><span class="kr">do</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">t</span> +<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="n">z</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">r</span></code></pre><p>This seems like an odd oversight.</p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> doesn’t seem able to cope with <code>do</code> blocks when <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is enabled. I reported this as <a href="https://ghc.haskell.org/trac/ghc/ticket/14471">an issue on the GHC bug tracker</a>, but it hasn’t received any attention, so it’s not likely to get fixed unless someone takes the initiative to do so.</p></li><li><p>Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> can cause problems with code that may have assumed <code>do</code> would always be monadic, and sometimes, that can cause code that typechecks to lead to an infinite loop at runtime. Specifically, if <code>do</code> notation is used to define <code>(&lt;*&gt;)</code> in terms of <code>(&gt;&gt;=)</code>, enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> will cause the definition of <code>(&lt;*&gt;)</code> to become self-referential and therefore divergent. Fortunately, this issue can be easily mitigated by simply writing <code>(&lt;*&gt;) = ap</code> instead, which is clearer and shorter than the equivalent code using <code>do</code>.</p></li></ol><p>Given all these things, it seems <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XApplicativeDo"><code>ApplicativeDo</code></a> is a little too new in a few places, and it isn’t quite baked. Still, I keep it enabled by default. Why? Well, <em>usually</em> it works fine without any problems, and when I run into issues, I can disable it on a per-module basis by writing <code>{-# LANGUAGE NoApplicativeDo #-}</code>. I still find that keeping it enabled by default is fine the vast majority of the time, I just sometimes need to work around the bugs.</p><p>In contrast, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures"><code>DefaultSignatures</code></a> isn’t buggy at all, as far as I can tell, it’s just not usually useful without fairly advanced features like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> (for type equalities) or <code>GHC.Generics</code>. I mostly use it for <a href="/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/">making lifting instances for <code>mtl</code>-style typeclasses easier to write</a>, which I’ve found to be a tiny bit tricky to explain (mostly due to the use of type equalities in the context), but it works well. I don’t see any real reason to leave this disabled, but if you don’t think you’re going to use it anyway, it doesn’t really matter one way or the other.</p><p>Finally, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms"><code>PatternSynonyms</code></a> allow users to extend the pattern language just as they are allowed to extend the value language. Bidirectional pattern synonyms are isomorphisms, and it’s quite useful to allow those isomorphisms to be used with Haskell’s usual pattern-matching syntax. I think this extension is actually quite benign, but I put it so low on this list because it seems infrequently used, and I get the sense most people consider it fairly advanced. I would argue, however, that it’s a very pleasant, useful extension, and it’s no more complicated than a number of the features in Haskell 98.</p><h3><a name="intermediate-extensions-to-the-haskell-type-system"></a>Intermediate extensions to the Haskell type system</h3><p>Now we’re getting into the meat of things. Everything up to this point has been, in my opinion, completely self-evident in its usefulness and simplicity. As far as I’m concerned, the extensions in the previous six sections have no business ever being left disabled. Starting in this section, however, I could imagine a valid argument being made either way.</p><p>The following three extensions add some complexity to the Haskell type system in return for some added expressive power:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a></p></li></ul><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification"><code>ExistentialQuantification</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> are related, given that the former is subsumed by the latter, but <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> also enables an alternative syntax. Both syntaxes allow packing away a typeclass dictionary or equality constraint that is brought into scope upon a successful pattern-match against a data constructor, something that is sometimes quite useful but certainly a departure from Haskell’s simple ADTs.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a> extend multi-parameter typeclasses, and they are almost unavoidable, given their use in the venerable <code>mtl</code> library. Like <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a>, <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a> add an additional layer of complexity to the typeclass system in order to express certain things that would otherwise be difficult or impossible.</p><p>All of these extensions involve a tradeoff. Enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> also implies <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds"><code>MonoLocalBinds</code></a>, which disables let generalization, one of the most likely ways a program that used to typecheck might subsequently fail to do so. Some might argue that this is a good reason to turn <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> on in a per-module basis, but I disagree: I actually want my language to be fairly consistent, and given that I know I am likely going to want to use <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XGADTs"><code>GADTs</code></a> <em>somewhere</em>, I want <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds"><code>MonoLocalBinds</code></a> enabled <em>everywhere</em>, not inconsistently and sporadically.</p><p>That aside, all these extensions are relatively safe. They are well-understood, and they are fairly self-contained extensions to the Haskell type system. I think these extensions have a very good power to cost ratio, and I find myself using them regularly (especially <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies"><code>FunctionalDependencies</code></a>), so I keep them enabled globally.</p><h3><a name="advanced-extensions-to-the-haskell-type-system"></a>Advanced extensions to the Haskell type system</h3><p>Finally, we arrive at the last set of extensions in this list. These are the most advanced features Haskell’s type system currently has to offer, and they are likely to be the most controversial to enable globally:</p><ul><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a></p></li><li><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a></p></li></ul><p>All of these extensions exist exclusively for the purpose of type-level programming. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a> allows datatype promotion, creating types that are always uninhabited and therefore can only be used phantom. <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> allows the definition of type-level functions that map types to other types. Both of these are minor extensions to Haskell’s surface area, but they have rather significant ramifications on the sort of programming that can be done and the way GHC’s typechecker must operate.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> is an interesting extension because it comes in so many flavors: associated type synonyms, associated datatypes, open and closed type synonym families, and open and closed datatype families. Associated types tend to be easier to grok and easier to use, though they can also be replaced by functional dependencies. Open type families are also quite similar to classes and instances, so they aren’t <em>too</em> tricky to understand. Closed type families, on the other hand, are a rather different beast, and they can be used to do fairly advanced things, <em>especially</em> in combination with <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds"><code>DataKinds</code></a>.</p><p>I happen to appreciate GHC’s support for these features, and while I’m hopeful that an eventual <code>DependentHaskell</code> will alleviate many of the existing infelicities with dependently typed programming in GHC, in the meantime, it’s often useful to enjoy what exists where practically applicable. Therefore, I have little problem keeping them enabled, since, like the vast majority of extensions on this list, these extensions merely lift restrictions, not adjust semantics of the language without the extensions enabled. When I am going to write a type family, I am going to turn on <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a>; I see no reason to annotate the modules in which I decide to do so. I do not write an annotation at the top of each module in which I define a typeclass or a datatype, so why should I do so with type families?</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilyDependencies"><code>TypeFamilyDependencies</code></a> is a little bit different, since it’s a very new extension, and it doesn’t seem to always work as well as I would hope. Still, when it doesn’t work, it fails with a very straightforward error message, and when it works, it is legitimately useful, so I don’t see any real reason to leave it off if <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies"><code>TypeFamilies</code></a> is enabled.</p><h3><a name="extensions-intentionally-left-off-this-list"></a>Extensions intentionally left off this list</h3><p>Given what I’ve said so far, it may seem like I would advocate flipping on absolutely every lever GHC has to offer, but that isn’t actually true. There are a few extensions I quite intentionally do <em>not</em> enable.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XUndecidableInstances"><code>UndecidableInstances</code></a> is something I turn on semi-frequently, since GHC’s termination heuristic is not terribly advanced, but I turn it on per-module, since it’s useful to know when it’s necessary (and in application code, it rarely is). <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XOverlappingInstances"><code>OverlappingInstances</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XIncoherentInstances"><code>IncoherentInstances</code></a>, in contrast, are completely banned—not only are they almost always a bad idea, GHC has a better, more fine-grained way to opt into overlapping instances, using the <code>{-# OVERLAPPING #-}</code>, <code>{-# OVERLAPPABLE #-}</code>, and <code>{-# INCOHERENT #-}</code> pragmas.</p><p><a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> and <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XQuasiQuotes"><code>QuasiQuotes</code></a> are tricky ones. Anecdotes seem to suggest that enabling <a href="https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell"><code>TemplateHaskell</code></a> everywhere leads to worse compile times, but after trying this on a few projects and measuring, I wasn’t able to detect any meaningful difference. Unless I manage to come up with some evidence that these extensions actually slow down compile times just by being <em>enabled</em>, even if they aren’t used, then I may add them to my list of globally-enabled extensions, since I use them regularly.</p><p>Other extensions I haven’t mentioned are probably things I just don’t use very often and therefore haven’t felt the need to include on this list. It certainly isn’t exhaustive, and I add to it all the time, so I expect I will continue to do so in the future. This is just what I have for now, and if your favorite extension isn’t included, it probably isn’t a negative judgement against that extension. I just didn’t think to mention it.</p><h2><a name="libraries-a-field-guide"></a>Libraries: a field guide</h2><p>Now that you’re able to build a Haskell project and have chosen which handpicked flavor of Haskell you are going to write, it’s time to decide which libraries to use. Haskell is an expressive programming language, and the degree to which different libraries can shape the way you structure your code is significant. Picking the right libraries can lead to clean code that’s easy to understand and maintain, but picking the wrong ones can lead to disaster.</p><p>Of course, there are <em>thousands</em> of Haskell libraries on Hackage alone, so I cannot hope to cover all of the ones I have ever found useful, and I certainly cannot cover ones that would be useful but I did not have the opportunity to try (of which there are certainly many). This blog post is long enough already, so I’ll just cover a few categories of libraries that I think I can offer interesting commentary on; most libraries can generally speak for themselves.</p><h3><a name="having-an-effect"></a>Having an effect</h3><p>One of the first questions Haskell programmers bump into when they begin working on a large application is how they’re going to model effects. Few practical programming languages are pure, but Haskell is one of them, so there’s no getting away from coming up with a way to manage side-effects.</p><p>For some applications, Haskell’s built-in solution might be enough: <code>IO</code>. This can work decently for data processing programs that do very minimal amounts of I/O, and the types of side-effects they perform are minimal. For these applications, most of the logic is likely to be pure, which means it’s already easy to reason about and easy to test. For other things, like web applications, it’s more likely that a majority of the program logic is going to be side-effectful by its nature—it may involve making HTTP requests to other services, interacting with a database, and writing to logfiles.</p><p>Figuring out how to structure these effects in a type-safe, decoupled, composable way can be tricky, especially since Haskell has so many different solutions. I could not bring myself to choose just one, but I did choose two: the so-called “<code>mtl</code> style” and freer monads.</p><p><code>mtl</code> style is so named because it is inspired by the technique of interlocking monadic typeclasses and lifting instances used to model effects using constraints that is used in the <a href="https://hackage.haskell.org/package/mtl"><code>mtl</code></a> library. Here is a small code example of what <code>mtl</code> style typeclasses and handlers look like:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MaybeT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="p">(</span><span class="s">"readFile: no such file "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="p">)</span> + +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InMemoryFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">delete</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="n">vfs</span></code></pre><p>This is the most prevalent way to abstract over effects in Haskell, and it’s been around for a long time. Due to the way it uses the typeclass system, it’s also very fast, since GHC can often specialize and inline the typeclass dictionaries to avoid runtime dictionary passing. The main drawbacks are the amount of boilerplate required and the conceptual difficulty of understanding exactly how monad transformers, monadic typeclasses, and lifting instances all work together to discharge <code>mtl</code> style constraints.</p><p>There are various alternatives to <code>mtl</code>’s direct approach to effect composition, most of which are built around the idea of reifying a computation as a data structure and subsequently interpreting it. The most popular of these is the <code>Free</code> monad, a clever technique for deriving a monad from a functor that happens to be useful for modeling programs. Personally, I think <code>Free</code> is overhyped. It’s a cute, mathematically elegant technique, but it involves a lot of boilerplate, and composing effect algebras is still a laborious process. The additional expressive power of <code>Free</code>, namely its ability to choose an interpreter dynamically, at runtime, is rarely necessary or useful, and it adds complexity and reduces performance for few benefits. (And in fact, this is still possible to do with <code>mtl</code> style, it’s just uncommon because there is rarely any need to do so.)</p><p>A 2017 blog post entitled <a href="https://markkarpov.com/post/free-monad-considered-harmful.html">Free monad considered harmful</a> discussed <code>Free</code> in comparison with <code>mtl</code> style, and unsurprisingly cast <code>Free</code> in a rather unflattering light. I largely agree with everything outlined in that blog post, so I will not retread its arguments here. I do, however, think that there is another abstraction that <em>is</em> quite useful: the so-called “freer monad” used to implement extensible effects.</p><p>Freer moves even further away from worrying about functors and monads, since its effect algebras do not even need to be functors. Instead, freer’s effect algebras are ordinary GADTs, and reusable, composable effect handlers are easily written to consume elements of these datatypes. Unfortunately, the way this works means that GHC is still not clever enough to optimize freer monads as efficiently as <code>mtl</code> style, since it can’t easily detect when the interpreter is chosen statically and use that information to specialize and inline effect implementations, but the cost difference is significantly reduced, and I’ve found that in real application code, the vast majority of the cost does not come from the extra overhead introduced by a more expensive <code>(&gt;&gt;=)</code>.</p><p>There are a few different implementations of freer monads, but I, sadly, was not satisfied with any of them, so I decided to contribute to the problem by creating yet another one. My implementation is called <a href="https://hackage.haskell.org/package/freer-simple"><code>freer-simple</code></a>, and it includes a streamlined API with <a href="https://hackage.haskell.org/package/freer-simple-1.0.1.1/docs/Control-Monad-Freer.html">more documentation than any other freer implementation</a>. Writing the above <code>mtl</code> style example using <code>freer-simple</code> is more straightforward:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="nb">()</span> + +<span class="nf">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Member</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">send</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span> + +<span class="nf">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Member</span><span class="w"> </span><span class="kt">FileSystem</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">send</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="nf">runFileSystemIO</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LastMember</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">r</span> +<span class="nf">runFileSystemIO</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">interpretM</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="kr">case</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> + +<span class="nf">runFileSystemInMemory</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="n">effs</span> +<span class="nf">runFileSystemInMemory</span><span class="w"> </span><span class="n">initVfs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runState</span><span class="w"> </span><span class="n">initVfs</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">FileSystem</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span><span class="w"> </span><span class="o">~&gt;</span><span class="w"> </span><span class="kt">Eff</span><span class="w"> </span><span class="p">(</span><span class="kt">State</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">effs</span><span class="p">)</span> +<span class="w"> </span><span class="n">fsToState</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">reinterpret</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">case</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">vfs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="p">(</span><span class="s">"readFile: no such file "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="p">)</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">vfs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">delete</span><span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="n">vfs</span></code></pre><p>(It could be simplified further with a little bit of Template Haskell to generate the <code>readFile</code> and <code>writeFile</code> function definitions, but I haven’t gotten around to writing that.)</p><p>So which effect system do I recommend? I used to recommend <code>mtl</code> style, but as of only two months ago, I now recommend <code>freer-simple</code>. It’s easier to understand, involves less boilerplate, achieves “good enough” performance, and generally gets out of the way wherever possible. Its API is designed to make it easy to do the sorts of the things you most commonly need to do, and it provides a core set of effects that can be used to build a real-world application.</p><p>That said, freer is indisputably relatively new and relatively untested. It has success stories, but <code>mtl</code> style is still the approach used by the majority of the ecosystem. <code>mtl</code> style has more library support, its performance characteristics are better understood, and it is a tried and true way to structure effects in a Haskell application. If you understand it well enough to use it, and you are happy with it in your application, my recommendation is to stick with it. If you find it confusing, however, or you end up running up against its limits, give <code>freer-simple</code> a try.</p><h3><a name="through-the-looking-glass-to-lens-or-not-to-lens"></a>Through the looking glass: to lens or not to lens</h3><p>There’s no getting around it: <a href="https://hackage.haskell.org/package/lens"><code>lens</code></a> is a behemoth of a library. For a long time, I wrote Haskell without it, and honestly, it worked out alright. I just wasn’t doing a whole lot of work that involved complicated, deeply-nested data structures, and I didn’t feel the need to bring in a library with such a reputation for having impenetrable operators and an almost equally impenetrable learning curve.</p><p>But, after some time, I decided I wanted to take the plunge. So I braced myself for the worst, pulled out my notebook, and started writing some code. To my surprise… it wasn’t that hard. It made sense. Sure, I still don’t know how it works on the inside, and I never did learn the majority of the exports in <code>Control.Lens.Operators</code>, but I had no need to. Lenses were useful in the way I had expected them to be, and so were prisms. One thing led to another, and before long, I understood the relationship between the various optics, the most notable additions to my toolkit being folds and traversals. Sure, the type errors were completely opaque much of the time, but I was able to piece things together with ample type annotations and time spent staring at ill-typed expressions. Before long, I had developed an intuition for <code>lens</code>.</p><p>After using it for a while, I retrospected on whether or not I liked it, and honestly, I still can’t decide. Some lensy expressions were straightforward to read and were a pleasant simplification, like this one:</p><pre><code class="pygments"><span class="nf">paramSpecs</span><span class="w"> </span><span class="o">^..</span><span class="w"> </span><span class="n">folded</span><span class="o">.</span><span class="n">_Required</span></code></pre><p>Others were less obviously improvements, such as this beauty:</p><pre><code class="pygments"><span class="kt">M</span><span class="o">.</span><span class="n">fromList</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">paramSpecs</span><span class="w"> </span><span class="o">^..</span><span class="w"> </span><span class="n">folded</span><span class="o">.</span><span class="n">_Optional</span><span class="o">.</span><span class="n">filtered</span><span class="w"> </span><span class="p">(</span><span class="n">has</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">_2</span><span class="o">.</span><span class="n">_UsePreviousValue</span><span class="p">)</span></code></pre><p>But operator soup aside, there was something deeper about <code>lens</code> that bothered me, and I just wasn’t sure what. I didn’t know how to articulate my vague feelings until I read a 2014 blog post entitled <a href="https://ro-che.info/articles/2014-04-24-lens-unidiomatic">Lens is unidiomatic Haskell</a>, which includes a point that I think is spot-on:</p><blockquote><p>Usually, types in Haskell are rigid. This leads to a distinctive style of composing programs: look at the types and see what fits where. This is impossible with <code>lens</code>, which takes overloading to the level mainstream Haskell probably hasn’t seen before.</p><p>We have to learn the new language of the <code>lens</code> combinators and how to compose them, instead of enjoying our knowledge of how to compose Haskell functions. Formally, <code>lens</code> types are Haskell function types, but while with ordinary Haskell functions you immediately see from types whether they can be composed, with <code>lens</code> functions this is very hard in practice.</p><p>[…]</p><p>Now let me clarify that this doesn’t necessarily mean that <code>lens</code> is a bad library. It’s an <em>unusual</em> library. It’s almost a separate language, with its own idioms, embedded in Haskell.</p></blockquote><p>The way <code>lens</code> structures its types deliberately introduces a sort of subtyping relationship—for example, all lenses are traversals and all traversals are folds, but not vice versa—and indeed, knowing this subtyping relationship is essential to working with the library and understanding how to use it. It is helpfully documented with a large diagram on <a href="https://hackage.haskell.org/package/lens">the <code>lens</code> package overview page</a>, and that diagram was most definitely an invaluable resource for me when I was learning how to use the library.</p><p>On the surface, this isn’t unreasonable. Subtyping is an enormously useful concept! The only reason Haskell dispenses with it entirely is because it makes type inference notoriously difficult. The subtyping relation between optics is one of the things that makes them so useful, since it allows you to easily compose a lens with a prism and get a traversal out. Unfortunately, the downside of all this is that Haskell does not truly have subtyping, so all of <code>lens</code>’s “types” really must be type aliases for types of roughly the same shape, namely functions. This makes type errors completely <em>baffling</em>, since the errors do not mention the aliases, only the fully-expanded types (which are often rather complicated, and their meaning is not especially clear without knowing how <code>lens</code> works under the hood).</p><p>So the above quote is correct: working with <code>lens</code> really <em>is</em> like working in a separate embedded language, but I’m usually okay with that. Embedded, domain-specific languages are good! Unfortunately, in this case, the host language is not very courteous to its guest. Haskell does not appear to be a powerful enough language for <code>lens</code> to be a language in its own right, so it must piggyback on top of Haskell’s error reporting mechanisms, which are insufficient for <code>lens</code> to be a cohesive linguistic abstraction. Just as debugging code by stepping through the assembly it produces (or, perhaps more relevant in 2018, debugging a compile-to-JS language by looking at the emitted JavaScript instead of the source code) makes for an unacceptably leaky language. We would never stand for such a thing in our general-purpose language tooling, and we should demand better even in our embedded languages.</p><p>That said, <code>lens</code> is just too useful to ignore. It is a hopelessly leaky abstraction, but it’s still an abstraction, and a powerful one at that. Given my selection of default extensions as evidence, I think it’s clear I have zero qualms with “advanced” Haskell; I will happily use even <code>singletons</code> where it makes sense. Haskell’s various language extensions are sometimes confusing in their own right, but their complexity is usually fundamental to the expressive power they bring. <code>lens</code> has some fundamental complexity, too, but it is mostly difficult for the wrong reasons. Still, while it is not the first library I reach for on every new Haskell project, manipulating nested data without <code>lens</code> is just too unpleasant after tasting the nectar, so I can’t advise against it in good faith.</p><p>Sadly, this means I’m a bit wishy-washy when it comes to using <code>lens</code>, but I do have at least one recommendation: if you decide to use <code>lens</code>, it’s better to go all-in. Don’t generate lenses for just a handful of datatypes, do it for <em>all</em> of them. You can definitely stick to a subset of the <code>lens</code> library’s features, but don’t apply it in some functions but not others. Having too many different, equally valid ways of doing things leads to confusion and inconsistency, and inconsistency minimizes code reuse and leads to duplication and spaghetti. Commit to using <code>lens</code>, or don’t use it at all.</p><h3><a name="mitigating-the-string-problem"></a>Mitigating the string problem</h3><p>Finally, Haskell has a problem with strings. Namely, <code>String</code> is a type alias for <code>[Char]</code>, a lazy, singly linked list of characters, which is an awful representation of text. Fortunately, the answer to this problem is simple: ban <code>String</code> in your programs.</p><p>Use <code>Text</code> everywhere. I don’t really care if you pick strict <code>Text</code> or lazy <code>Text</code>, but pick one and stick to it. Don’t ever use <code>String</code>, and <em>especially</em> don’t ever, <em>ever</em>, <em><strong>ever</strong></em> use <code>ByteString</code> to represent text! There are enormously few legitimate cases for using <code>ByteString</code> in a program that is not explicitly about reading or writing raw data, and even at that level, <code>ByteString</code> should only be used at program boundaries. In that sense, I treat <code>ByteString</code> much the same way I treat <code>IO</code>: push it to the boundaries of your program.</p><p>One of Haskell’s core tenets is making illegal states unrepresentable. Strings are not especially useful datatypes for this, since they are sequences of arbitrary length made up of atoms that can be an enormously large number of different things. Still, string types enforce a very useful invariant, a notion of a sequence of human-readable characters. In the presence of Unicode, this is a more valuable abstraction than it might seem, and the days of treating strings as little different from sequences of bytes are over. While strings make a poor replacement for enums, they are quite effective at representing the incredible amount of text humans produce in a staggeringly large number of languages, and they are the right type for that job.</p><p><code>ByteString</code>, on the other hand, is essentially never the right type for any job. If a type classifies a set of values, <code>ByteString</code> is no different from <code>Any</code>. It is the structureless type, the all-encompassing blob of bits. A <code>ByteString</code> could hold anything at all—some text, an image, an executable program—and the type system certainly isn’t going to help to answer that question. The only use case I can possibly imagine for passing around a <code>ByteString</code> in your program rather than decoding it into a more precise type is if it truly holds opaque data, e.g. some sort of token or key provided by a third party with no structure guaranteed whatsoever. Still, even this should be wrapped in a <code>newtype</code> so that the type system enforces this opaqueness.</p><p>Troublingly, <code>ByteString</code> shows up in many libraries’ APIs where it has no business being. In many cases, this seems to be things where ASCII text is expected, but this is hardly a good reason to willingly accept absolutely anything and everything! Make an <code>ASCII</code> type that forbids non-ASCII characters, and provide a <code>ByteString -&gt; Maybe ASCII</code> function. Alternatively, think harder about your problem in question to properly support Unicode as you almost certainly ought to.</p><p>Other places <code>ByteString</code> appears are similarly unfortunate. Base-64 encoding, for example, could be given the wonderfully illustrative type <code>ByteString -&gt; Text</code>, or even <code>ByteString -&gt; ASCII</code>! Such a type makes it immediately clear why base-64 is useful: it allows transforming arbitrary binary data into a reliable textual encoding. If we consider that <code>ByteString</code> is essentially <code>Any</code>, this function has the type <code>Any -&gt; ASCII</code>, which is amazingly powerful! We can convert <em>anything</em> to ASCII text!</p><p>Existing libraries, however, just provide the boring, disappointingly inaccurate type <code>ByteString -&gt; ByteString</code>, which is one of the most useless types there is. It is essentially <code>Any -&gt; Any</code>, the meaningless function type. It conveys nothing about what it does, other than that it is pure. Giving a function this type is scarcely better than dynamic typing. Its mere existence is a failure of Haskell library design.</p><p>But wait, it gets worse! <code>Data.Text.Encoding</code> exports a function called <code>decodeUtf8</code>, which has type <code>ByteString -&gt; Text</code>. What an incredible function with a captivating type! Whatever could it possibly do? Again, this function’s type is basically <code>Any -&gt; Text</code>, which is remarkable in the power it gives us. Let’s try it out, shall we?</p><pre><code>ghci&gt; decodeUtf8 "\xc3\x28" +"*** Exception: Cannot decode byte '\x28': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream +</code></pre><p>Oh. Well, that’s a disappointment.</p><p>Haskell’s string problem goes deeper than <code>String</code> versus <code>Text</code>; it seems to have wound its way around the collective consciousness of the Haskell community and made it temporarily forget that it cares about types and totality. This isn’t that hard, I swear! I can only express complete befuddlement at how many of these APIs are just completely worthless.</p><p>Fortunately, there is a way out, and that way out is <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a>. It is the first Haskell library I ever wrote. It provides <em>type safe</em>, <em>total</em> conversions between <code>Text</code> and various other types, and it is encoding aware. It provides appropriately-typed base-16 and base-64 conversion functions, and is guaranteed to never raise any exceptions. Use it, and apply the Haskell philosophy to your strings, just as you already do for everything else in your program.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p><em>Phew.</em></p><p>When I started writing this blog post, it used the phrase “short overview” in the introduction. It is now over ten thousand words long. I think that’s all I have it in me to say for now.</p><p>Haskell is a wonderful language built by a remarkable group of people. Its community is often fraught with needlessly inflammatory debates about things like the value of codes of conduct, the evils of Hackage revisions, and precisely how much or how little people ought to care about the monad laws. These flame wars frustrate me to no end, and they sometimes go so far as to make me ashamed to call myself a part of the Haskell community. Many on the “outside” seem to view Haskellers as an elitist, mean-spirited cult, more interested in creating problems for itself than solving them.</p><p>That perception is categorically wrong.</p><p>I have never been in a community of programmers so dedicated and passionate about applying thought and rigor to building software, then going out and <em>actually doing it</em>. I don’t know anywhere else where a cutting-edge paper on effect systems is discussed by the very same people who are figuring out how to reliably deploy distributed services to AWS. Some people view the Haskell community as masturbatory, and to some extent, they are probably right. One of my primary motivators for writing Haskell is that it is fun and it challenges me intellectually in ways that other languages don’t. But that challenge is not a sign of uselessness, it is a sign that Haskell is <em>so close</em> to letting me do the right thing, to solving the problem the right way, to letting me work without compromises. When I write in most programming languages, I must constantly accept that my program will never be robust in all the ways I want it to be, and I might as well give up before I even start. Haskell’s greatest weakness is that it tempts me to try.</p><p>Haskell is imperfect, as it will always be. I doubt I will ever be satisfied by any language or any ecosystem. There will always be more to learn, more to discover, better tools and abstractions to develop. Many of them will not look anything like Haskell; they may not involve formal verification or static types or effect systems at all. Perhaps live programming, structural editors, and runtime hotswapping will finally take over the world, and we will find that the problems we thought we were solving were irrelevant to begin with. I can’t predict the future, and while I’ve found great value in the Haskell school of program construction, I dearly hope that we do not develop such tunnel vision that we cannot see that there may be other ways to solve these problems. Many of the solutions are things we likely have not even begun to think about. Still, whether that happens or not, it is clear to me that Haskell is a point in the design space unlike any other, and we learn almost as much from the things it gets wrong as we do from the things it gets right.</p><p>It’s been a wonderful two years, Haskell. I won’t be a stranger.</p><ol class="footnotes"></ol></article>Hackett progress report: documentation, quality of life, and snakehttps://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/https://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/28 Aug 2017<article><p>Three months ago, <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">I wrote a blog post describing my new, prototype implementation of my programming language, Hackett</a>. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/"><strong>the first (albeit quite incomplete) approach to Hackett’s documentation</strong></a>.</p><p>I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.</p><h2><a name="a-philosophy-of-documentation"></a>A philosophy of documentation</h2><p>Racket, as a project, has always had <a href="http://docs.racket-lang.org">wonderful documentation</a>. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably <a href="http://docs.racket-lang.org/scribble/index.html">Scribble, the Racket documentation tool</a>. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with <a href="http://docs.racket-lang.org/scribble/plt-manuals.html">a powerful library for documenting Racket code</a>. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.</p><p>Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an <code>@</code> character instead of <code>\</code>. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use <a href="http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29">the provided <code>defproc</code> form</a>:</p><pre><code class="pygments"><span class="n">@defproc</span><span class="p">[(</span><span class="nb">add1</span><span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="nb">number?</span><span class="p">])</span><span class="w"> </span><span class="nb">number?</span><span class="p">]{</span> +<span class="n">Returns</span><span class="w"> </span><span class="n">@racket</span><span class="p">[(</span><span class="nb">+</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="mi">1</span><span class="p">)]</span><span class="o">.</span><span class="p">}</span></code></pre><p>This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into <a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29">something like this</a>:</p><p><a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29"></a></p><p>The fact that Scribble documents are fully-fledged <em>programs</em> equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is <a href="http://docs.racket-lang.org/scribble/eval.html">the <code>scribble/example</code> module</a>, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special <code>eval:check</code> form that enables <a href="https://docs.python.org/3/library/doctest.html">doctest</a>-like checking, which allows documentation to serve double duty as a test suite.</p><p>Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is <em>designed</em> with the goal of supporting many different programming languages, and Scribble is no exception. Things like <a href="http://docs.racket-lang.org/scribble/eval.html"><code>scribble/example</code></a> essentially work out of the box with Hackett, and most of <a href="http://docs.racket-lang.org/scribble/plt-manuals.html"><code>scribble/manual</code></a> can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The <code>defproc</code> and <code>defstruct</code> forms are hardly builtins; they are defined as part of the <code>scribble/manual</code> library in terms of Scribble primitives, and <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt">we can do the same</a>.</p><p>Hackett’s documentation already defines three new forms, <code>defdata</code>, <code>defclass</code>, and <code>defmethod</code>, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the <code>Functor</code> typeclass using <code>defclass</code> like this:</p><pre><code class="pygments"><span class="n">@defclass</span><span class="p">[(</span><span class="n">Functor</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="nb">map</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="p">)})]]{</span> + +<span class="n">A</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">@deftech</span><span class="p">{</span><span class="n">functors</span><span class="p">}</span><span class="o">,</span><span class="w"> </span><span class="n">essentially</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="k">provide</span><span class="w"> </span><span class="n">a</span> +<span class="n">mapping</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">“piercing”</span><span class="w"> </span><span class="n">operation.</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">@racket</span><span class="p">[</span><span class="nb">map</span><span class="p">]</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">viewed</span><span class="w"> </span><span class="n">in</span> +<span class="n">different</span><span class="w"> </span><span class="n">ways:</span> + +<span class="k">...</span><span class="p">}</span></code></pre><p>With only a little more than the above code, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29">Hackett’s documentation includes a beautifully-typeset definition of the <code>Functor</code> typeclass</a>, including examples and rich prose:</p><p><a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29"></a></p><p>Scribble makes Hackett’s documentation shine.</p><h3><a name="a-tale-of-two-users"></a>A tale of two users</h3><p>For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.</p><p>A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.</p><p>However, for experienced users, an authoritative reference is <em>exactly</em> what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.</p><p>This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:</p><ul><li><p>Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.</p><p><a href="https://docs.oracle.com/javase/8/docs/api/">Java’s API documentation</a> documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly <em>small</em> language that does not often change.</p><p>On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java <em>does</em> provide guide-style documentation in the form of the <a href="https://docs.oracle.com/javase/tutorial/">Java Tutorials</a>, but they are of inconsistent quality.</p><p>More importantly, while the Java tutorials link to the API docs, the reverse is <strong>not</strong> true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.</p><p>Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.</p></li><li><p>Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.</p><p>The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is <a href="https://developer.mozilla.org/en-US/">MDN</a>. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.</p><p>The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.</p><p>This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. <a href="http://ramdajs.com/docs/">Ramda’s documentation is nothing but a reference</a>, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, <a href="http://passportjs.org/docs">Passport’s docs are essentially <em>only</em> a set of tutorials</a>, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method <em>does</em>. Fortunately, <a href="https://facebook.github.io/react/docs/hello-world.html">there are some libraries, like React</a>, that absolutely <em>nail</em> this, and they have both styles of documentation that are <strong>actually cross-referenced</strong>. Unfortunately, those are mostly the exceptions, not the norm.</p></li><li><p><a href="https://docs.python.org/3/index.html">Python’s documentation is interesting</a>, since it includes a set of tutorials alongside the API reference, and it <em>also</em> ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on <code>if</code> links to the section in the reference about <code>if</code>, but nothing goes in the other direction, which is something of a missed opportunity.</p></li><li><p><a href="https://hackage.haskell.org/package/base">Haskell manages to be especially bad here</a> (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.</p><p>It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and <a href="https://www.haskell.org/haddock/">Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look</a>.</p></li></ul><p>The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with <em>two</em> pieces of documentation: the <a href="http://docs.racket-lang.org/guide/index.html">Racket Guide</a> and the <a href="http://docs.racket-lang.org/reference/index.html">Racket Reference</a>. The guide includes over <strong>one hundred thousand</strong> words of explanations and examples, and the reference includes roughly <strong>half a million</strong>. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.</p><p>Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the <a href="http://docs.racket-lang.org/hackett/guide.html">Hackett Guide</a> and the <a href="http://docs.racket-lang.org/hackett/reference.html">Hackett Reference</a>, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily <em>need</em> to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a <em>lot</em> more accessible without making it any less useful for power users.</p><h2><a name="rounding-hackett-s-library-sanding-its-edges"></a>Rounding Hackett’s library, sanding its edges</h2><p>One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a <em>lot</em> of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.</p><p>Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:</p><ul><li><p>Hackett includes a <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29"><code>Double</code></a> type for working with IEEE 754 double-precision floating-point numbers.</p></li><li><p>Local definitions are supported via the <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29"><code>let</code></a> and <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29"><code>letrec</code></a> forms.</p></li><li><p>The prelude includes many more functions, especially <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29">functions on lists</a>.</p></li><li><p>The Hackett reader has been adjusted to support using <code>.</code> as a bare symbol, since <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29"><code>.</code> is the function composition operator</a>.</p></li><li><p>The Hackett REPL supports many more forms, including <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29">ADT</a>, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29">class</a>, and <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29">instance</a> definitions. Additionally, the REPL now uses <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a> instances to display the results of expressions. To compensate for the inability to print non-<a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a>able things, a new <code>(#:type expr)</code> syntax is permitted to print the type of <em>any</em> expression.</p></li><li><p>Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.</p></li></ul><p>Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189">heavily commented</a> with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.</p><p>One of the most exciting things about documenting Hackett has been realizing just <em>how much</em> already exists. Seriously, if you have gotten to this point in the blog post but haven’t read <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/">the actual documentation</a> yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for <em>me</em>: it’s a language I’d like to use.</p><p>Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!</p><h2><a name="a-demo-with-pictures"></a>A demo with pictures</h2><p>Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?</p><p>Hackett ships with a special package of demo libraries in the aptly-named <code>hackett-demo</code> package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">the previous Hackett blog post</a>, I demonstrated the capabilities of <code>hackett/demo/web-server</code>. In this blog post, we’re going to use <code>hackett/demo/pict</code> and <code>hackett/demo/pict/universe</code>, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!</p><p>As always, we’ll start with <code>#lang hackett</code>, and we’ll import the necessary libraries:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/pict</span> +<span class="w"> </span><span class="n">hackett/demo/pict/universe</span><span class="p">)</span></code></pre><p>With that, we can start immediately with a tiny example. Just to see how <code>hackett/demo/pict</code> works, let’s start by rendering a red square. We can do this by writing a <code>main</code> action that calls <code>print-pict</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))))</span></code></pre><p>If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!</p><p></p><p>Using the REPL, we can inspect the type of <code>print-pict</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">print-pict</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Unit</span><span class="p">))</span></code></pre><p>Unsurprisingly, displaying a picture to the screen needs <code>IO</code>. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of <code>filled-square</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">filled-square</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">Pict</span><span class="p">)</span></code></pre><p>No <code>IO</code> to be seen! This is because “picts” are entirely <em>pure</em> values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">{(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))</span> +<span class="w"> </span><span class="n">hc-append</span> +<span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))}))</span></code></pre><p>This code will print out a red square to the left of a blue one.</p><p></p><p>Again, <code>hc-append</code> is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">hc-append</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="n">Pict</span><span class="p">))</span></code></pre><p>Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!</p><h3><a name="implementing-a-snake-clone"></a>Implementing a snake clone</h3><p>This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the <code>pict</code> or <code>universe</code> libraries. Hopefully it’s still illustrative.</p><p>We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an <code>Eq</code> instance for our points.</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="n">d:left</span><span class="w"> </span><span class="n">d:right</span><span class="w"> </span><span class="n">d:up</span><span class="w"> </span><span class="n">d:down</span><span class="p">)</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">Eq</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="k">==</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)]</span><span class="w"> </span><span class="p">{{</span><span class="n">a</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">c</span><span class="p">}</span><span class="w"> </span><span class="n">&amp;&amp;</span><span class="w"> </span><span class="p">{</span><span class="n">b</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">d</span><span class="p">}})])</span></code></pre><p>With these two datatypes, we can implement a <code>move</code> function that accepts a point and a direction and produces a new point for an adjacent tile:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Direction</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:left</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:right</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:up</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">})]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:down</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">})])</span></code></pre><p>The next step is to define a type for our world state. The <code>big-bang</code> library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span> +<span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="c1">; snake direction</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; snake blocks</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; food blocks</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">set-ws-direction</span><span class="w"> </span><span class="p">[[</span><span class="n">d</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)])</span></code></pre><p>Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-width</span><span class="w"> </span><span class="mi">50</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-height</span><span class="w"> </span><span class="mi">30</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="p">{(</span><span class="n">d*</span><span class="w"> </span><span class="mf">15.0</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">integer-&gt;double</span><span class="p">})</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="p">(</span><span class="n">blank-rect</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-height</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">13.0</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="n">block</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="n">block</span><span class="p">))</span></code></pre><p>Now we can write our actual <code>render</code> function. To do this, we simply need to render each <code>Point</code> in our <code>World-State</code>’s two lists as a block on an <code>empty-board</code>. We’ll write a helper function, <code>render-on-board</code>, which does exactly that:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render-on-board</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Pict</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">pict</span><span class="w"> </span><span class="n">points</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">foldr</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">acc</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">pict</span><span class="p">))</span> +<span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="n">points</span><span class="p">)])</span></code></pre><p>This function uses <code>foldr</code> to collect each point and place the provided pict at the right location using <code>pin-over</code> on an empty board. Using <code>render-on-board</code>, we can write the <code>render</code> function in just a couple of lines:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)</span> +<span class="w"> </span><span class="mf">0.0</span><span class="w"> </span><span class="mf">0.0</span> +<span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="n">food-points</span><span class="p">))])</span></code></pre><p>Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the <code>big-bang</code> library provides a <code>random-integer</code> function, which we can use to write a <code>random-point</code> action:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">random-point</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">point</span><span class="w"> </span><span class="n">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span> +<span class="w"> </span><span class="n">&lt;*&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-height</span><span class="p">)})</span></code></pre><p>Hackett supports applicative notation using infix operators, so <code>random-point</code> looks remarkably readable. It also runs in <code>IO</code>, since the result is, obviously, random. Fortunately, the <code>on-tick</code> function runs in <code>IO</code> as well (unlike <code>render</code>, which must be completely pure), so we can use <code>random-point</code> when necessary to generate a new food block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">init!</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)})</span> +<span class="w"> </span><span class="p">{</span><span class="nb">reverse</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tail!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="nb">reverse</span><span class="p">})</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">new-snake-point</span><span class="w"> </span><span class="p">(</span><span class="n">move</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">(</span><span class="n">head!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">elem?</span><span class="w"> </span><span class="n">food-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">random-point</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">snake-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">delete</span><span class="w"> </span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">food-points</span><span class="p">)})))</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">init!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)}</span> +<span class="w"> </span><span class="n">food-points</span><span class="p">))))])</span></code></pre><p>This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to <code>new-snake-point</code>, then checks if there is a food block at that location. If there is, it generates a <code>new-food-point</code>, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.</p><p>The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our <code>set-ws-direction</code> function that we wrote earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-key</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">KeyEvent</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:left</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:left</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:right</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:right</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:up</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:up</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:down</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:down</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="k">_</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id</span><span class="p">}])</span></code></pre><p>The <code>on-key</code> function runs in <code>IO</code>, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in <code>pure</code>.</p><p>We’re almost done now—all we need to do is set up the <em>initial</em> state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">initial-state</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">initial-food</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">sequence</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="n">repeat</span><span class="w"> </span><span class="n">random-point</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d:right</span> +<span class="w"> </span><span class="p">{(</span><span class="n">point</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">24</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">23</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">nil</span><span class="p">}</span> +<span class="w"> </span><span class="n">initial-food</span><span class="p">))))</span></code></pre><p>Notably, we can use the <code>repeat</code> function to create an infinite list of <code>random-point</code> actions, <code>take</code> the first five of them, then call <code>sequence</code> to execute them from left to right. Now, all we have to do is put the pieces together in a <code>main</code> block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">state</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">initial-state</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">big-bang</span><span class="w"> </span><span class="n">state</span> +<span class="w"> </span><span class="kd">#:to-draw</span><span class="w"> </span><span class="n">render</span> +<span class="w"> </span><span class="kd">#:on-tick</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="mf">0.2</span> +<span class="w"> </span><span class="kd">#:on-key</span><span class="w"> </span><span class="n">on-key</span><span class="p">)))</span></code></pre><p>And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.</p><p></p><h2><a name="contributing-to-hackett"></a>Contributing to Hackett</h2><p>If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.</p><p>On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, <a href="https://github.com/lexi-lambda/hackett/issues">Hackett has an issue tracker</a>, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.</p><p>Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to <a href="https://github.com/lexi-lambda/hackett/issues">open an issue on GitHub</a>, send me a message on the <code>#racket</code> IRC channel on Freenode, or ping me on <a href="http://racket-slack.herokuapp.com">the Racket Slack team</a>.</p><h2><a name="acknowledgements"></a>Acknowledgements</h2><p>Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to <a href="https://github.com/gelisam">Samuel Gélineau, aka gelisam</a>, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.</p><p>Additionally, I want to specially thank <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.</p><p>Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.</p><ol class="footnotes"></ol></article>Unit testing effectful Haskell with monad-mockhttps://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/https://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/29 Jun 2017<article><p>Nearly eight months ago, <a href="/blog/2016/10/03/using-types-to-unit-test-in-haskell/">I wrote a blog post about unit testing effectful Haskell code</a> using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, <a href="https://hackage.haskell.org/package/monad-mock">monad-mock</a>.</p><h2><a name="a-first-glance-at-monad-mock"></a>A first glance at monad-mock</h2><p>The monad-mock library is, first and foremost, designed to be <em>easy</em>. It doesn’t ask much from you, and it requires almost zero boilerplate.</p><p>The first step is to write an mtl-style interface that encodes an effect you want to mock. For example, you might want to test some code that interacts with the filesystem:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Now you just have to write your code as normal. For demonstration purposes, here’s a function that defines copying a file in terms of <code>readFile</code> and <code>writeFile</code>:</p><pre><code class="pygments"><span class="nf">copyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>Making this function work on the real filesystem is trivial, since we just need to define an instance of <code>MonadFileSystem</code> for <code>IO</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span></code></pre><p>But how do we test this? Well, we <em>could</em> run some real code in <code>IO</code>, which might not be so bad for such a simple function, but this seems like a bad idea. For one thing, a bad implementation of <code>copyFile</code> could do some pretty horrible things if it misbehaved and decided to overwrite important files, and if you’re constantly running a test suite whenever a file changes, it’s easy to imagine causing a lot of damage. Running tests against the real filesystem also makes tests slower and harder to parallelize, and it only gets much worse once you are doing more complex effects than interacting with the filesystem.</p><p>Using monad-mock, we can test this function in just a couple of lines of code:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Exception</span><span class="w"> </span><span class="p">(</span><span class="nf">evaluate</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock.TH</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Data.Function</span><span class="w"> </span><span class="p">((</span><span class="o">&amp;</span><span class="p">))</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Test.Hspec</span> + +<span class="nf">makeMock</span><span class="w"> </span><span class="s">"FileSystemAction"</span><span class="w"> </span><span class="p">[</span><span class="n">ts</span><span class="o">|</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="o">|</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"copyFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reads a file and writes its contents to another file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span></code></pre><p>That’s it!</p><p>The last two lines of the above snippet are the real interesting bits, which specify the actions that are expected to be executed, and it couples them with their results. You will find that if you tweak the list in any way, such as reordering the actions, eliminating one or both of them, or adding an additional action to the end, the test will fail. We could even turn this into a property-based test that generated arbitrary file paths and file contents.</p><p>Admittedly, in this trivial example, the mock is a little silly, since converting this into a property-based test would demonstrate how much we’ve basically just reimplemented the function in our test. However, once our function starts to do somewhat more complicated things, then our tests become more meaningful. Here’s a similar function that only copies a file if it is nonempty:</p><pre><code class="pygments"><span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This function has some logic which is very clearly <em>not</em> expressed in its type, and it would be difficult to encode that information into the type in a safe way. Fortunately, we can guarantee that it works by writing some tests:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">""</span><span class="w"> </span><span class="p">]</span></code></pre><p>These tests are much more useful, and they have some actual value to them. Imagine we had accidentally written <code>when</code> instead of <code>unless</code>, an easy typo to make. Our tests would fail with some useful error messages:</p><pre><code>1) copyNonemptyFile copies a file with contents + uncaught exception: runMockT: expected the following unexecuted actions to be run: + WriteFile "bar.txt" "contents" + +2) copyNonemptyFile does nothing with an empty file + uncaught exception: runMockT: expected end of program, called writeFile + given action: WriteFile "bar.txt" "" +</code></pre><p>You now know enough to write tests with monad-mock.</p><h2><a name="why-unit-test"></a>Why unit test?</h2><p>When the issue of testing is brought up in Haskell, it is often treated with a certain distaste by a portion of the community. There are some points I’ve seen a number of times, and though they take different forms, they boil down to two ideas:</p><ol><li><p>“Haskell code does not need tests because the type system can prove correctness.”</p></li><li><p>“Testing in Haskell is trivial because it is a pure language, and testing pure functions is easy.”</p></li></ol><p>I’ve been writing Haskell professionally for over a year now, and I can happily say that there <em>is</em> some truth to both of those things! When my Haskell code typechecks, I feel a confidence in it that I would not feel were I using a language with a less powerful type system. Furthermore, Haskell encourages a “pure core, impure shell” approach to system design that makes testing many things pleasant and straightforward, and it completely eliminates the worry of subtle nondeterminism leaking into tests.</p><p>That said, Haskell is not a proof assistant, and its type system cannot guarantee everything, especially for code that operates on the boundaries of what Haskell can control. For much the same reason, I find that my pure code is the code I am <em>least</em> likely to need to test, since it is also the code with the strongest type safety guarantees, operating on types in my application’s domain. In contrast, the effectful code is often what I find the most value in extensively testing, since it often contains the most subtle complexity, and it is frequently difficult or even impossible to encode into types.</p><p>Haskell has the power to provide remarkably strong correctness guarantees with a surprisingly small amount of effort by using a combination of tests and types, using each to accommodate for the other’s weaknesses and playing to each technique’s strengths. Some code is test-driven, other code is type-driven. Most code ends up being a mix of both. Testing is just a tool like any other, and it’s nice to feel confident in one’s ability to effectively structure code in a decoupled, testable manner.</p><h2><a name="why-mock"></a>Why mock?</h2><p>Even if you accept that testing is good, the question of whether or not to <em>mock</em> is a subtler issue. To some people, “unit testing” is synonymous with mocks. This is emphatically not true, and in fact, overly aggressive mocking is one of the best ways to make your test suite completely worthless. The monad-mock approach to mocking is a bit more principled than mocking in many dynamic, object-oriented languages, but it comes with many of the same drawbacks: mocks couple your tests to your implementation in ways that make them less valuable and less meaningful.</p><p>For the <code>MonadFileSystem</code> example above, I would actually probably <em>not</em> use a mock. Instead, I would use a <strong>fake</strong>, in-memory filesystem implementation:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)])</span> +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">second</span><span class="w"> </span><span class="n">sort</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">runStateT</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">fs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="o">&amp;</span> +<span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"readFile: no such file ‘"</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"’"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">filter</span><span class="w"> </span><span class="p">((</span><span class="o">/=</span><span class="w"> </span><span class="n">path</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fst</span><span class="p">)</span><span class="w"> </span><span class="n">fs</span></code></pre><p>The above snippet demonstrates how easy it is to define a <code>MonadFileSystem</code> implementation in terms of <code>StateT</code>, and while this may seem like a lot of boilerplate, it really isn’t. You have to write a fake <em>once</em> per interface, and the above block is a minuscule twelve lines of code. With this technique, you are still able to write tests that depend on the state of the filesystem before and after running the implementation, but you decouple yourself from the precise process of getting there:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"bar.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">),</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is better than using a mock, and I would highly recommend doing it if you can! However, a lot of real applications have to interact with services of much greater complexity than an idealized filesystem, and creating that sort of in-memory fake is not always practical. One such situation might be interacting with AWS CloudFormation, for example:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">createStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackName</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StackTemplate</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackId</span><span class="p">)</span> +<span class="w"> </span><span class="n">listStacks</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="p">[</span><span class="kt">StackSummaries</span><span class="p">])</span> +<span class="w"> </span><span class="n">describeStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackId</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackInfo</span><span class="p">)</span> +<span class="w"> </span><span class="c1">-- and so on...</span></code></pre><p>AWS is a very complex system, and it can do dozens of different things (and fail in dozens of different ways) based on an equally complex set of inputs. For example, in the above API, <code>createStack</code> needs to parse its template, which can be YAML or JSON, in order to determine which of many possible errors and behaviors can be produced, both on the initial call and on subsequent ones.</p><p>Creating a fake implementation of <em>AWS</em> is hardly feasible, and this is where a mock can be useful. By simply writing <code>makeMock "AWSAction" [ts| MonadAWS |]</code>, we can test functions that interact with AWS in a pure way without necessarily needing to replicate all of its complexity.</p><h3><a name="isolating-mocks"></a>Isolating mocks</h3><p>Of course, tests that use mocks provide less value than tests that use “smarter” fakes, since they are far more tightly coupled to the implementation, and it’s dramatically more likely that you will need to change the tests when you change the logic. To avoid this, it can be helpful to create multiple interfaces to the same thing: a high-level interface and a low-level one. If our above <code>MonadAWS</code> is a low-level interface, we could create a high-level counterpart that does precisely what our application needs:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDeploy</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">executeDeployment</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>When running our application “for real”, we would use <code>MonadAWS</code> to implement <code>MonadDeploy</code>:</p><pre><code class="pygments"><span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>The nice thing about this is we can actually test <code>executeDeploymentImpl</code> using a <code>MonadAWS</code> mock, so we can still have unit test coverage of the code on the boundaries of our system! Additionally, by containing the mock to a single place, we can test the rest of our code using a smarter fake implementation of <code>MonadDeploy</code>, helping to decouple our code from AWS’s complex API and improve the reliability and usefulness of our test suite.</p><p>They key point here is that mocking is just a small piece of the larger testing puzzle in <em>any</em> language, and that is just as true in Haskell. An overemphasis on mocking is an easy way to end up with a test suite that feels useless, probably because it is. Use mocks as a technique to insulate your application from the complexity in others’ APIs, then use more domain-specific testing techniques and type-level assertions to ensure the correctness of your logic.</p><h2><a name="how-monad-mock-works"></a>How monad-mock works</h2><p>If you’ve read this far and are convinced that monad-mock is useful, you may safely stop reading now. However, if you are interested in the details of what it actually does and what makes it tick, the rest of this blog post is going to focus on how the implementation works and how it compares to other techniques.</p><p>The centerpiece of monad-mock’s API is its monad transformer, <code>MockT</code>, which is a type constructor that accepts three types:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="p">)</span></code></pre><p>The <code>m</code> and <code>a</code> type variables obviously correspond to the usual monad transformer arguments, which represent the underlying monad and the result of the monadic computation, respectively. The <code>f</code> variable is more interesting, since it’s what makes <code>MockT</code> work at all, and it isn’t even a type: it’s a type constructor with kind <code>* -&gt; *</code>. What does it mean?</p><p>Looking at the type signature of <code>runMockT</code> gives us a little bit more information about what that <code>f</code> actually represents:</p><pre><code class="pygments"><span class="nf">runMockT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>This type signature provides two pieces of key information:</p><ol><li><p>The <code>f</code> parameter is constrained by the <code>Action f</code> constraint.</p></li><li><p>Running a mocked computation requires supplying a list of <code>WithResult f</code> values. This list corresponds to the list of expectations provided to <code>runMock</code> in earlier examples.</p></li></ol><p>To understand both of these things, it helps to examine the definition of an actual datatype that can have an <code>Action</code> instance. For the filesystem example, the action datatype looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Notice how each constructor clearly corresponds to one of the methods of <code>MonadFileSystem</code>, with a type to match. Now the purpose of the type provided to the <code>FileSystemAction</code> constructor (in this case <code>r</code>) should hopefully become clear: it represents the type of the value <em>produced</em> by each method. Also note that the type is completely phantom—it does not appear in negative position in any of the constructors.</p><p>With this in mind, we can take a look at the definition of <code>WithResult</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="p">(</span><span class="kt">:-&gt;</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span></code></pre><p>This is what defines the <code>(:-&gt;)</code> constructor from earlier in the blog post, and you can see that it effectively just represents a tuple of an action and a value of its associated result. It’s completely type-safe, since it ensures the result matches the type argument to the action.</p><p>Finally, this brings us to the <code>Action</code> class, which is not complex, but is unfortunately necessary:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">eqAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="n">showAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Notice that these methods are effectively just <code>(==)</code> and <code>show</code>, lifted to type constructors of kind <code>* -&gt; *</code>. One significant difference is that <code>eqAction</code> produces <code>Maybe (a :~: b)</code> instead of <code>Bool</code>, where <code>(:~:)</code> is from <code>Data.Type.Equality</code>. This is a type equality witness, which means a successful equality between two values allows the compiler to be sure that the two <em>types</em> are equal. This is necessary for the implementation of <code>runMockT</code> due to the phantom type in actions—in order to convince GHC that we can properly return the result of a mocked action, we need to assure it that the value we’re going to return is actually of the proper type.</p><p>Implementing this typeclass is not particularly burdensome, but it’s entirely boilerplate, so even if you want to define your own action type (that is, you don’t want to use <code>makeMock</code>), you can use the <code>deriveAction</code> function from <code>Control.Monad.Mock.TH</code> to derive an <code>Action</code> instance on an existing datatype.</p><h3><a name="connecting-the-mock-to-its-class"></a>Connecting the mock to its class</h3><p>Now that we have an action with which to mock a class, we need to actually define an instance of that class for <code>MockT</code>. For this process, monad-mock provides a <code>mockAction</code> function with the following type:</p><pre><code class="pygments"><span class="nf">mockAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span></code></pre><p>This function accepts two arguments: the name of the method being mocked and the action that represents the current call. This is easier to illustrate with an actual instance of <code>MonadFileSystem</code> using <code>MockT</code> and our <code>FileSystemAction</code> type:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MockT</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"readFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"writeFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>This allows <code>readFile</code> and <code>writeFile</code> to defer to the mock, and providing the names of the functions as strings helps monad-mock to produce useful error messages upon failure. Internally, <code>MockT</code> is a <code>StateT</code> that keeps track of a list of <code>WithResult f</code> values as its state. Each call to the mock checks the action against the internal list of calls, and if they match, it returns the associated result. Otherwise, it throws an exception.</p><p>This scheme is simple, but it seems to work remarkably well. There are some obvious enhancements that will probably be eventually necessary, like allowing action results that run in the underlying monad <code>m</code> in order to support things like <code>throwError</code> from <code>MonadError</code>, but so far, it hasn’t been necessary for what we’ve been using it for. Certain tricky signatures defy this simple technique, such as signatures where a monadic action appears in a negative position (that is, the signatures you need things like <a href="https://hackage.haskell.org/package/monad-control">monad-control</a> or <a href="https://hackage.haskell.org/package/monad-unlift">monad-unlift</a> for), but we’ve found that most of our effects don’t have any reason to include such signatures.</p><h2><a name="a-brief-comparison-with-free-r-monads"></a>A brief comparison with free(r) monads</h2><p>At this point, astute readers will likely be thinking about free monads, which parts of this technique greatly resemble. The representation of actions as GADTs is especially similar to <a href="https://hackage.haskell.org/package/freer">freer</a>, which does something extremely similar. Indeed, you can think of this technique as something that combines a freer-style representation with mtl-style classes. Given that freer already does this, you might ask yourself what the point is.</p><p>If you are already sold on free monads, monad-mock may very well be uninteresting to you. From the perspective of theoretical novelty, monad-mock is not anything new or different. However, there are a variety of practical reasons to prefer mtl over free, and it’s nice to see how easy it is to enjoy the testing benefits of free without too much extra effort.</p><p>An in-depth comparison between mtl and free is well outside the scope of this blog post. However, the key point is that this technique <em>only</em> affects test code, so the real runtime implementation will not be affected in any way. This means you can take advantage of the performance benefits and ecosystem support of mtl without sacrificing simple, expressive testing.</p><h2><a name="conclusion"></a>Conclusion</h2><p>To cap things off, I want to emphasize monad-mock’s role as a single part of a larger initiative we’ve been making for the better part of the past eighteen months. Haskell is a language with ever-evolving techniques and style, and it’s sometimes dizzying to figure out how to use all the pieces together to develop robust, maintainable applications. While monad-mock might not be anything drastically different from existing testing techniques, my hope is that it can provide an opinionated mechanism to make testing easy and accessible, even for complex interactions with other services and systems.</p><p>I’ve made an effort to make it abundantly clear in this blog post that monad-mock is <em>not</em> a silver bullet to testing, and in fact, I would prefer other techniques for ensuring correctness whenever possible. Even so, mocking is a nice tool to have in your toolbox, and it’s a good fallback to get even the worst APIs under test coverage.</p><p>If you want to try out monad-mock for yourself, <a href="https://hackage.haskell.org/package/monad-mock">take a look at the documentation on Hackage</a> and start playing around! It’s still early software, so it’s not the most proven or featureful, but we’ve managed to get mileage out of it already, all the same. If you find any problems, have a use case it does not support, or just find something about it unclear, please do not hesitate to <a href="https://github.com/cjdev/monad-mock">open an issue on the GitHub repository</a>—we obviously can’t fix issues we don’t know about.</p><p>Thanks as always to the many people who have contributed ideas that have shaped my philosophy and approach to testing and have helped provide the tools that make this library work. Happy testing!</p><ol class="footnotes"></ol></article>Realizing Hackett, a metaprogrammable Haskellhttps://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/https://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/27 May 2017<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Lifts for free: making mtl typeclasses derivablehttps://lexi-lambda.github.io/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/https://lexi-lambda.github.io/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/28 Apr 2017<article><p>Perhaps the most important abstraction a Haskell programmer must understand to effectively write modern Haskell code, beyond the level of the monad, is the <em>monad transformer</em>, a way to compose monads together in a limited fashion. One frustrating downside to monad transformers is a proliferation of <code>lift</code>s, which explicitly indicate which monad in a transformer “stack” a particular computation should run in. Fortunately, the venerable <a href="https://hackage.haskell.org/package/mtl">mtl</a> provides typeclasses that make this lifting mostly automatic, using typeclass machinery to insert <code>lift</code> where appropriate.</p><p>Less fortunately, the mtl approach does not actually eliminate <code>lift</code> entirely, it simply moves it from use sites to instances. This requires a small zoo of extraordinarily boilerplate-y instances, most of which simply implement each typeclass method using <code>lift</code>. While we cannot eliminate the instances entirely without somewhat dangerous techniques like <a href="https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#overlapping-instances">overlapping instances</a>, we <em>can</em> automatically derive them using features of modern GHC, eliminating the truly unnecessary boilerplate.</p><h2><a name="the-problem-with-mtl-style-typeclasses"></a>The problem with mtl-style typeclasses</h2><p>To understand what problem it is exactly that we’re trying to solve, we first need to take a look at an actual mtl-style typeclass. I am going to start with an mtl-<em>style</em> typeclass, rather than an actual typeclass in the mtl, due to slight complications with mtl’s actual typeclasses that we’ll get into later. Instead, let’s start with a somewhat boring typeclass, which we’ll call <code>MonadExit</code>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">System.Exit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitCode</span><span class="p">)</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is a simple typeclass that abstracts over the concept of early exit, given an exit code. The most obvious implementation of this typeclass is over <code>IO</code>, which will actually exit the program:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="k">qualified</span><span class="w"> </span><span class="nn">System.Exit</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">IO</span><span class="w"> </span><span class="p">(</span><span class="n">exitWith</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IO</span><span class="o">.</span><span class="n">exitWith</span></code></pre><p>One of the cool things about these typeclasses, though, is that we don’t have to have just one implementation. We could also write a pure implementation of <code>MonadExit</code>, which would simply short-circuit the current computation and return the <code>ExitCode</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">ExitCode</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span></code></pre><p>Instead of simply having an instance on a concrete monad, though, we probably want to be able to use this in a larger monad stack, so we can define an <code>ExitT</code> monad transformer that can be inserted into any monad transformer stack:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Except</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="p">,</span><span class="w"> </span><span class="nf">runExceptT</span><span class="p">,</span><span class="w"> </span><span class="nf">throwError</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Trans</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTrans</span><span class="p">)</span> + +<span class="nf">runExitT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">runExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span></code></pre><p>With this in place, we can write actual programs using our <code>ExitT</code> monad transformer:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">runExitT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">putStrLn</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">putStrLn</span><span class="w"> </span><span class="s">"world"</span> +<span class="nf">hello</span> +<span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span></code></pre><p>This is pretty cool! Unfortunately, experienced readers will see the rather large problem with what we have so far. Specifically, it won’t actually work if we try and wrap <code>ExitT</code> in another monad transformer:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runExitT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="n">runReaderT</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">password</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">"password1234"</span><span class="p">)</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="c1">-- super secure password</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"access granted"</span> + +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"not the right password"</span> +<span class="o">&lt;</span><span class="n">interactive</span><span class="o">&gt;:</span><span class="w"> </span><span class="ne">error</span><span class="kt">:</span> +<span class="w"> </span><span class="err">•</span><span class="w"> </span><span class="kt">No</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="n">for</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">[</span><span class="kt">Char</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m0</span><span class="p">)))</span> +<span class="w"> </span><span class="n">arising</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">use</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="err">‘</span><span class="n">it</span><span class="err">’</span> +<span class="w"> </span><span class="err">•</span><span class="w"> </span><span class="kt">In</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">stmt</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">interactive</span><span class="w"> </span><span class="kt">GHCi</span><span class="w"> </span><span class="n">command</span><span class="kt">:</span><span class="w"> </span><span class="n">print</span><span class="w"> </span><span class="n">it</span></code></pre><p>The error message is relatively self-explanatory if you are familiar with mtl error messages: there is no <code>MonadExit</code> instance for <code>ReaderT</code>. This makes sense, since we only defined a <code>MonadExit</code> instance for <em><code>ExitT</code></em>, nothing else. Fortunately, the instance for <code>ReaderT</code> is completely trivial, since we just need to use <code>lift</code> to delegate to the next monad in the stack:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>Now that the delegating instance is set up, we can actually use our <code>logIn</code> function:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"not the right password"</span> +<span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitFailure</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">logIn</span><span class="w"> </span><span class="s">"password1234"</span> +<span class="kt">Right</span><span class="w"> </span><span class="s">"access granted"</span></code></pre><h3><a name="an-embarrassment-of-instances"></a>An embarrassment of instances</h3><p>We’ve managed to make our program work properly now, but we’ve still only defined the delegating instance for <code>ReaderT</code>. What if someone wants to use <code>ExitT</code> with <code>WriterT</code>? Or <code>StateT</code>? Or any of <code>ExceptT</code>, <code>RWST</code>, or <code>ContT</code>? Well, we have to define instances for each and every one of them, and as it turns out, the instances are all identical!</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ContT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>This is bad enough on its own, but this is actually the <em>simplest</em> case: a typeclass with a single method which is trivially lifted through any other monad transformer. Another thing we’ve glossed over is actually defining all the delegating instances for the <em>other</em> mtl typeclasses on <code>ExitT</code> itself. Fortunately, we can derive these ones with <code>GeneralizedNewtypeDeriving</code>, since <code>ExceptT</code> has already done most of the work for us:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="w"> </span><span class="c1">-- base</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="c1">-- transformers-base</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTrans</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="c1">-- mtl</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadThrow</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadCatch</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadMask</span><span class="w"> </span><span class="c1">-- exceptions</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBaseControl</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="c1">-- monad-control</span> +<span class="w"> </span><span class="p">)</span></code></pre><p>Unfortunately, we have to write the <code>MonadError</code> instance manually if we want it, since we don’t want to pick up the instance from <code>ExceptT</code>, but rather wish to defer to the underlying monad. This means writing some truly horrid delegation code:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span> + +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExitT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="kt">ExitT</span><span class="w"> </span><span class="n">x&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x&#39;</span></code></pre><p>(Notably, this is so awful because <code>catchError</code> is more complex than the simple <code>exitWith</code> method we’ve studied so far, which is why we’re starting with a simpler typeclass. We’ll get more into this later, as promised.)</p><p>This huge number of instances is sometimes referred to as the “n<sup>2</sup> instances” problem, since it requires every monad transformer have an instance of every single mtl-style typeclass. Fortunately, in practice, this proliferation is often less horrible than it might seem, mostly because deriving helps a lot. However, remember that if <code>ExitT</code> <em>weren’t</em> a simple wrapper around an existing monad transformer, we wouldn’t be able to derive the instances at all! Instead, we’d have to write them all out by hand, just like we did with all the <code>MonadExit</code> instances.</p><p>It’s a shame that these typeclass instances can’t be derived in a more general way, allowing derivation for arbitrary monad transformers instead of simply requiring the newtype deriving machinery. As it turns out, with clever use of modern GHC features, we actually <strong>can</strong>. It’s not even all that hard.</p><h2><a name="default-instances-with-default-signatures"></a>Default instances with default signatures</h2><p>It’s not hard to see that our <code>MonadExit</code> instances are all exactly the same: just <code>lift . exitWith</code>. Why is that, though? Well, every instance is an instance on a monad transformer over a monad that is already an instance of <code>MonadExit</code>. In fact, we can express this in a type signature, and we can extract <code>lift . exitWith</code> into a separate function:</p><pre><code class="pygments"><span class="nf">defaultExitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">defaultExitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>However, writing <code>defaultExitWith</code> really isn’t any easier than writing <code>lift . exitWith</code>, so this deduplication doesn’t really buy us anything. However, it <em>does</em> indicate that we could write a default implementation of <code>exitWith</code> if we could require just a little bit more from the implementing type. With <a href="https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#default-method-signatures">GHC’s <code>DefaultSignatures</code> extension</a>, we can do precisely that.</p><p>The idea is that we can write a separate type signature for a default implementation of <code>exitWith</code>, which can be more specific than the type signature for <code>exitWith</code> in general. This allows us to use our <code>defaultExitWith</code> implementation more or less directly:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DefaultSignatures #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>We have to use <code>m1</code> instead of <code>m</code>, since type variables in the instance head are always scoped, and the names would conflict. However, this creates another problem, since our specialized type signature replaces <code>m</code> with <code>t m1</code>, which won’t quite work (as GHC can’t automatically figure out they should be the same). Instead, we can use <code>m</code> in the type signature, then just add a type equality constraint ensuring that <code>m</code> and <code>t m1</code> must be the same type:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">exitWith</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">exitWith</span></code></pre><p>Now we can write all of our simple instances without even needing to write a real implementation! All of the instance bodies can be empty:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="p">(</span><span class="kt">ContT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>While this doesn’t completely alleviate the pain of writing instances, it’s definitely an improvement over what we had before. With <a href="https://downloads.haskell.org/~ghc/8.2.1-rc1/docs/html/users_guide/glasgow_exts.html#deriving-strategies">GHC 8.2’s new <code>DerivingStrategies</code> extension</a>, it becomes especially beneficial when defining entirely new transformers that should also have <code>ExitT</code> instances, since they can be derived with <code>DeriveAnyClass</code>:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="n">anyclass</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadExit</span><span class="p">)</span></code></pre><p>This is pretty wonderful.</p><p>Given that only <code>MonadExit</code> supports being derived in this way, we sadly still need to implement the other, more standard mtl-style typeclasses ourselves, like <code>MonadIO</code>, <code>MonadBase</code>, <code>MonadReader</code>, <code>MonadWriter</code>, etc. However, what if all of those classes provided the same convenient default signatures that our <code>MonadExit</code> does? If that were the case, then we could write something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ParserT</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Text</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="n">anyclass</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadBase</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadThrow</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadCatch</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadMask</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadExit</span> +<span class="w"> </span><span class="p">)</span></code></pre><p>Compared to having to write all those instances by hand, this would be a pretty enormous difference. Unfortunately, many of these typeclasses are not quite as simple as our <code>MonadExit</code>, and we’d have to be a bit more clever to make them derivable.</p><h2><a name="making-mtl-s-classes-derivable"></a>Making mtl’s classes derivable</h2><p>Our <code>MonadExit</code> class was extremely simple, since it only had a single method with a particularly simple type signature. For reference, this was the type of our generic <code>exitWith</code>:</p><pre><code class="pygments"><span class="nf">exitWith</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadExit</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ExitCode</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Let’s now turn our attention to <code>MonadReader</code>. At first blush, this typeclass should not be any trickier to implement than <code>MonadExit</code>, since the types of <code>ask</code> and <code>reader</code> are both quite simple:</p><pre><code class="pygments"><span class="nf">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="nf">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>However, the type of the other method, <code>local</code>, throws a bit of a wrench in our plans. It has the following type signature:</p><pre><code class="pygments"><span class="nf">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Why is this so much more complicated? Well, the key is in the second argument, which has the type <code>m a</code>. That’s not something that can be simply <code>lift</code>ed away! Try it yourself: try to write a <code>MonadReader</code> instance for some monad transformer. It’s not as easy as it looks!</p><p>We can illustrate the problem by creating our own version of <code>MonadReader</code> and implementing it for something like <code>ExceptT</code> ourselves. We can start with the trivial methods first:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span></code></pre><p>However, implementing <code>local</code> is harder. Let’s specialize the type signature to <code>ExceptT</code> to make it more clear why:</p><pre><code class="pygments"><span class="nf">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Our base monad, <code>m</code>, implements <code>local</code>, but we have to convert the first argument from <code>ExceptT e m a</code> into <code>m (Either e a)</code> first, run it through <code>local</code> in <code>m</code>, then wrap it back up in <code>ExceptT</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ExceptT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">runExceptT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>This operation is actually a mapping operation of sorts, since we’re mapping <code>local f</code> over <code>x</code>. For that reason, this can be rewritten using the <code>mapExceptT</code> function provided from <code>Control.Monad.Except</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">reader</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapExceptT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">local</span></code></pre><p>If you implement <code>MonadReader</code> instances for other transformers, like <code>StateT</code> and <code>WriterT</code>, you’ll find that the instances are exactly the same <em>except</em> for <code>mapExceptT</code>, which is replaced with <code>mapStateT</code> and <code>mapWriterT</code>, respectively. This is sort of obnoxious, given that we want to figure out how to create a generic version of <code>local</code> that works with any monad transformer, but this requires concrete information about which monad we’re in. Obviously, the power <code>MonadTrans</code> gives us is not enough to make this generic. Fortunately, there is a typeclass which does: <a href="http://hackage.haskell.org/package/monad-control-1.0.1.0/docs/Control-Monad-Trans-Control.html#t:MonadTransControl"><code>MonadTransControl</code></a> from the <code>monad-control</code> package.</p><p>Using <code>MonadTransControl</code>, we can write a generic <code>mapT</code> function that maps over an arbitrary monad transformer with a <code>MonadTransControl</code> instance:</p><pre><code class="pygments"><span class="nf">mapT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="p">(</span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="p">),</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">b</span> +<span class="nf">mapT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">return</span></code></pre><p>This type signature may look complicated (and, well, it is), but the idea is that the <code>StT</code> associated type family encapsulates the monadic state that <code>t</code> introduces. For example, for <code>ExceptT</code>, <code>StT (ExceptT e) a</code> is <code>Either e a</code>. For <code>StateT</code>, <code>StT (StateT s) a</code> is <code>(a, s)</code>. Some transformers, like <code>ReaderT</code>, have no state, so <code>StT (ReaderT r) a</code> is just <code>a</code>.</p><p>I will not go into the precise mechanics of how <code>MonadTransControl</code> works in this blog post, but it doesn’t matter significantly; the point is that we can now use <code>mapT</code> to create a generic implementation of <code>local</code> for use with <code>DefaultSignatures</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span> +<span class="w"> </span><span class="n">ask</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">ask</span> + +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">local</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">local</span> + +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">ask</span></code></pre><p>Once more, we now get instances of our typeclass, in this case <code>MonadReader</code>, <strong>for free</strong>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>It’s also worth noting that we <em>don’t</em> get a <code>ContT</code> instance for free, even though <code>ContT</code> has a <code>MonadReader</code> instance in mtl. Unlike the other monad transformers mtl provides, <code>ContT</code> does not have a <code>MonadTransControl</code> instance because it cannot be generally mapped over. While a <code>mapContT</code> function does exist, its signature is more restricted:</p><pre><code class="pygments"><span class="nf">mapContT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ContT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ContT</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>It happens that <code>local</code> can still be implemented for <code>ContT</code>, so it can still have a <code>MonadReader</code> instance, but it cannot be derived in the same way as it can for the other transformers. Still, in practice, I’ve found that most user-defined transformers do not have such complex control flow, so they can safely be instances of <code>MonadTransControl</code>, and they get this deriving for free.</p><h3><a name="extending-this-technique-to-other-mtl-typeclasses"></a>Extending this technique to other mtl typeclasses</h3><p>The default instances for the other mtl typeclasses are slightly different from the one for <code>MonadReader</code>, but for the most part, the same general technique applies. Here’s a derivable <code>MonadError</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">throwError</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">throwError</span> + +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">e</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">catchError</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">f</span><span class="p">))</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">return</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadError</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="kt">RWST</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>The <code>MonadState</code> interface turns out to be extremely simple, so it doesn’t even need <code>MonadTransControl</code> at all:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="n">get</span> + +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">put</span> + +<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">state</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">get</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">s&#39;</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">s</span> +<span class="w"> </span><span class="n">put</span><span class="w"> </span><span class="n">s&#39;</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadState</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">(</span><span class="kt">WriterT</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="p">)</span></code></pre><p>Everything seems to be going well! However, not everything is quite so simple.</p><h3><a name="a-monadwriter-diversion"></a>A <code>MonadWriter</code> diversion</h3><p>Unexpectedly, <code>MonadWriter</code> turns out to be by far the trickiest of the bunch. It’s not too hard to create default implementations for most of the methods of the typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="p">(</span><span class="kt">Monoid</span><span class="w"> </span><span class="n">w</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writer</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">writer</span> + +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTrans</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">lift</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tell</span> + +<span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="kr">default</span><span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span> +<span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="p">(</span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">listen</span><span class="w"> </span><span class="p">(</span><span class="n">run</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="n">y&#39;</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="p">(</span><span class="n">return</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">y&#39;</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="p">)</span></code></pre><p>However, <code>MonadWriter</code> has a fourth method, <code>pass</code>, which has a particularly tricky type signature:</p><pre><code class="pygments"><span class="nf">pass</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>As far as I can tell, this is not possible to generalize using <code>MonadTransControl</code> alone, since it would require inspection of the result of the monadic argument (that is, it would require a function from <code>StT t (a, b) -&gt; (StT t a, b)</code>), which is not possible in general. My gut is that this could likely also be generalized with a slightly more powerful abstraction than <code>MonadTransControl</code>, but it is not immediately obvious to me what that abstraction should be.</p><p>One extremely simple way to make this possible would be to design something to serve this specific use case:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">RunSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="o">.</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">StT</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">MonadTransControl</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">(</span><span class="kt">RunSplit</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>Instances of <code>MonadTransSplit</code> would basically just provide a way to pull out bits of the result, if possible:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">ExceptT</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">e</span><span class="p">,</span><span class="w"> </span><span class="kt">Nothing</span><span class="p">)</span> +<span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftWith</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">fmap</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">run</span><span class="p">)</span> +<span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">),</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">),</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">y</span><span class="p">)</span></code></pre><p>Then, using this, it would be possible to write a generic version of <code>pass</code>:</p><pre><code class="pygments"><span class="kr">default</span><span class="w"> </span><span class="n">pass</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadTransSplit</span><span class="w"> </span><span class="n">t</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="n">m1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="n">m1</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">w</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span> +<span class="nf">pass</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftWithSplit</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">run</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pass</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="kr">case</span> +<span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">f</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="kt">Nothing</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="n">restoreT</span><span class="w"> </span><span class="p">(</span><span class="n">return</span><span class="w"> </span><span class="n">r</span><span class="p">)</span></code></pre><p>However, this seems pretty overkill for just one particular method, given that I have no idea if <code>MonadTransSplit</code> would be useful <em>anywhere</em> else. One interesting thing about going down this rabbit hole, though, is that I learned that <code>pass</code> has some somewhat surprising behavior when mixed with transformers like <code>ExceptT</code> or <code>MaybeT</code>, if you don’t carefully consider how it works. It’s a strange method with a somewhat strange interface, so I don’t think I have a satisfactory conclusion about <code>MonadWriter</code> yet.</p><h2><a name="regrouping-and-stepping-back"></a>Regrouping and stepping back</h2><p>Alright, that was a lot of fairly intense, potentially confusing code. What the heck did we actually accomplish? Well, we got a couple of things:</p><ol><li><p>First, we developed a technique for writing simple mtl-style typeclasses that are derivable using <code>DeriveAnyClass</code> (or simply writing an empty instance declaration). We used a <code>MonadExit</code> class as a proof of concept, but really, the technique is applicable to most mtl-style typeclasses that represent simple effects (including, for example, <code>MonadIO</code>).</p><p>This technique is useful in isolation, even if you completely disregard the rest of the blog post. For an example where I recently applied it in real code, see <a href="https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L226-L271">the default signatures provided with <code>MonadPersist</code> from the <code>monad-persist</code> library</a>, which make <a href="https://github.com/cjdev/monad-persist/blob/1ce8568d881da3171f8689dd65f4f2df5f6dd313/library/Control/Monad/Persist.hs#L506-L513">defining instances completely trivial</a>. If you use mtl-style typeclasses in your own application to model effects, I don’t see much of a reason <em>not</em> to use this technique.</p></li><li><p>After <code>MonadExit</code>, we applied the same technique to the mtl-provided typeclasses <code>MonadReader</code>, <code>MonadError</code>, and <code>MonadState</code>. These are a bit trickier, since the first two need <code>MonadTransControl</code> in addition to the usual <code>MonadTrans</code>.</p><p>Whether or not this sort of thing should actually be added to mtl itself probably remains to be seen. For the simplest typeclass, <code>MonadState</code>, it seems like there probably aren’t many downsides, but given the difficulty implementing it for <code>MonadWriter</code> (or, heaven forbid, <code>MonadCont</code>, which I didn’t even seriously take a look at for this blog post), it doesn’t seem like an obvious win. Consistency is important.</p><p>Another downside that I sort of glossed over is possibly even more significant from a practical point of view: adding default signatures to <code>MonadReader</code> would require the removal of the default implementation of <code>ask</code> that is provided by the existing library (which implements <code>ask</code> in terms of <code>reader</code>). This would be backwards-incompatible, so it’d be difficult to change, even if people wanted to do it. Still, it’s interesting to consider what these typeclasses might look like if they were designed today.</p></li></ol><p>Overall, these techniques are not a silver bullet for deriving mtl-style typeclasses, nor do they eliminate the n<sup>2</sup> instances problem that mtl style suffers from. That said, they <em>do</em> significantly reduce boilerplate and clutter in the simplest cases, and they demonstrate how modern Haskell’s hierarchy of typeclasses provides a lot of power, both to describe quite abstract concepts and to alleviate the need to write code by hand.</p><p>I will continue to experiment with the ideas described in this blog post, and I’m sure some more pros and cons will surface as I explore the design space. If you have any suggestions for how to deal with “the <code>MonadWriter</code> problem”, I’d be very interested to hear them! In the meantime, consider using the technique in your application code when writing effectful, monadic typeclasses.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questionshttps://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/https://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/05 Jan 2017<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheseshttps://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/https://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/02 Jan 2017<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Using types to unit-test in Haskellhttps://lexi-lambda.github.io/blog/2016/10/03/using-types-to-unit-test-in-haskell/https://lexi-lambda.github.io/blog/2016/10/03/using-types-to-unit-test-in-haskell/03 Oct 2016<article><p>Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.</p><p>Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.</p><h2><a name="first-an-aside-on-testing-philosophy"></a>First, an aside on testing philosophy</h2><p>Testing methodology is a controversial topic within the larger programming community, and there are a multitude of different approaches. This blog post is about <em>unit testing</em>, an already nebulous term with a number of different definitions. For the purposes of this post, I will define a unit test as a test that stubs out collaborators of the code under test in some way. Accomplishing that in Haskell is what this is primarily about.</p><p>I want to be clear that I do not think that unit tests are the only way to write tests, nor the best way, nor even always an applicable way. Depending on your domain, rigorous unit testing might not even make sense, and other forms of tests (end-to-end, integration, benchmarks, etc.) might fulfill your needs.</p><p>In practice, though, implementing those other kinds of tests seems to be well-documented in Haskell compared to pure, object-oriented style unit testing. As my Haskell applications have grown, I have found myself wanting a more fine-grained testing tool that allows me to both test a piece of my codebase in isolation and also use my domain-specific types. This blog post is about that.</p><p>With that disclaimer out of the way, let’s talk about testing in Haskell.</p><h2><a name="drawing-seams-using-types"></a>Drawing seams using types</h2><p>One of the primary attributes of unit tests in object-oriented languages, especially statically-typed ones, is the concept of “seams” within a codebase. These are internal boundaries between components of a system. Some boundaries are obvious—interactions with a database, manipulation of the file system, and performing I/O over the network, to name a few examples—but others are more subtle. Especially in larger codebases, it can be helpful to isolate two related but distinct pieces of functionality as much as possible, which makes them easier to reason about, even if they’re actually part of the same codebase.</p><p>In OO languages, these seams are often marked using interfaces, whether explicitly (in the case of static languages) or implicitly (in the case of dynamic ones). By programming to an interface, it’s possible to create “fake” implementations of that interface for use in unit tests, effectively making it possible to stub out code that isn’t directly relevant to the code being tested.</p><p>In Haskell, representing these seams is a lot less obvious. Consider a fairly trivial function that reverses a file’s contents on the file system:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span></code></pre><p>This function is impossible to test without testing against a real file system. It simply performs I/O directly, and there’s no way to “mock out” the file system for testing purposes. Now, admittedly, this function is so trivial that a unit test might not seem worth the cost, but consider a slightly more complicated function that interacts with a database:</p><pre><code class="pygments"><span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>It might now be a bit more clear that it could be useful to test the above function without running a real database and doing all the necessary context setup before each test case. Indeed, it would be nice if a test could just provide stubbed implementations for <code>fetchUser</code> and <code>fetchRecentPosts</code>, then make assertions about the output.</p><p>One way to solve this problem is to pass the results of those two functions to <code>renderUserProfile</code> as arguments, turning it into a pure function that could be easily tested. This becomes obnoxious for functions of even just slightly more complexity, though (it is not unreasonable to imagine needing a handful of different queries to render a user’s profile page), and it requires significantly restructuring code simply because the tests need it.</p><p>The above code is not only difficult to test, however—it has another problem, too. Specifically, both functions return <code>IO</code> values, which means they can effectively do <em>anything</em>. Haskell has a very strong type system for typing terms, but it doesn’t provide any guarantees about effects beyond a simple yes/no answer about function purity. Even though the <code>renderUserProfile</code> function should really only need to interact with the database, it could theoretically delete files, send emails, make HTTP requests, or do any number of other things.</p><p>Fortunately, it’s possible to solve <em>both</em> problems—a lack of testability and a lack of type safety—using the same general technique. This approach is reminiscent of the interface-based seams of object-oriented languages, but unlike most object-oriented approaches, it provides additional type safety guarantees without the need to explicitly modify the code to support some kind of dependency injection.</p><h3><a name="making-implicit-interfaces-explicit"></a>Making implicit interfaces explicit</h3><p>Statically typed, object-oriented languages provide interfaces as a language construct to encode certain kinds of contracts into the type system, and Haskell has something similar. Typeclasses are, in many ways, an analog to OO interfaces, and they can be used in a similar way. In the above case, let’s write down interfaces that the <code>reverseFile</code> and <code>renderUserProfile</code> functions can use:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span></code></pre><p>The really nice thing about these interfaces is that our function implementations don’t have to change <em>at all</em> to take advantage of them. In fact, all we have to change is their types:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span> + +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty neat, since we haven’t had to alter our code at all, but we’ve managed to completely decouple ourselves from <code>IO</code>. This has the direct effect of both making our code more abstract (we no longer rely on the “real” file system or a “real” database, which makes our code easier to test) and restricting what our functions can do (just from looking at the type signatures, we know what side-effects they can perform).</p><p>Of course, since we’re now coding against an interface, our code doesn’t actually do much of anything. If we want to actually use the functions we’ve written, we’ll have to define instances of <code>MonadFS</code> and <code>MonadDB</code>. When actually running our code, we’ll probably still use <code>IO</code> (or some monad transformer stack with <code>IO</code> at the bottom), so we can define trivial instances for that existing use case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchUser</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchRecentPosts</span></code></pre><p>Even if we go no further, <strong>this is already incredibly useful</strong>. By restricting the sorts of effects our functions can perform at the type level, it becomes a lot easier to see which code is interacting with what. This can be invaluable when working in a part of a moderately large codebase that you are unfamiliar with. Even if the only instance of these typeclasses is <code>IO</code>, the benefits are immediately apparent.</p><p>Of course, this blog post is about testing, so we’re going to go further and take advantage of these seams we’ve now drawn. The question is: how?</p><h2><a name="testing-with-typeclasses-an-initial-attempt"></a>Testing with typeclasses: an initial attempt</h2><p>Given that we now have functions depending on an interface instead of <code>IO</code>, we can create separate instances of our typeclasses for use in tests. Let’s start with the <code>renderUserProfile</code> function. We’ll create a simple wrapper around the <code>Identity</code> type, since we don’t actually care much about the “effects” of our <code>MonadDB</code> methods:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Functor.Identity</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">unTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now, we’ll create a trivial instance of <code>MonadDB</code> for <code>TestM</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa"</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">Post</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">postTitle</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span></code></pre><p>With this instance, it’s now possible to write a simple unit test of the <code>renderUserProfile</code> function that doesn’t need a real database running at all:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"renderUserProfile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows the user’s name"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="s">"Alyssa’s Profile"</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows a list of the user’s posts"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">li</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty nice, and running the above tests reveals a nice property of these kinds of isolated test cases: the test suite runs <em>really, really fast</em>. Communicating with a database, even in extremely simple ways, takes a measurable amount of time, especially with dozens of tests. In contrast, even with hundreds of tests, our unit test suite runs in less than a tenth of a second.</p><p>This all seems to be successful, so let’s try and apply the same testing technique to <code>reverseFile</code>.</p><h3><a name="testing-side-effectful-code"></a>Testing side-effectful code</h3><p>Looking at the type signature for <code>reverseFile</code>, we have a small problem:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Specifically, the return type is <code>()</code>. Making any assertions against the result of this function would be completely worthless, given that it’s guaranteed to be the same exact thing each time. Instead, <code>reverseFile</code> is inherently side-effectful, so we want to be able to test that it properly interacts with the file system in the correct way.</p><p>In order to do this, a simple wrapper around <code>Identity</code> won’t be enough, but we can replace it with something more powerful: <code>Writer</code>. Specifically, we can use a writer monad to “log” what gets called in order to test side-effects. We’ll start by creating a new <code>TestM</code> type, just like last time:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">])</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span></code></pre><p>Using this slightly more powerful type, we can write a useful instance of <code>MonadFS</code> that will track the argument given to <code>writeFile</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span></code></pre><p>Again, the instance is quite simple, but it now enables us to write a straightforward unit test for <code>reverseFile</code>:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span></code></pre><p>Again, quite simple to both implement and use, and the test itself is blindingly fast. There’s another problem, though, which is that we have technically left part of <code>reverseFile</code> untested: we’ve completely ignored the <code>path</code> argument.</p><p>In this contrived example, it may seem silly to test something so trivial, but in real code, it’s quite possible that one would care very much about testing multiple different aspects about a single function. When testing <code>renderUserProfile</code>, this was not hard, since we could reuse the same <code>TestM</code> type and <code>MonadDB</code> instance for both test cases, but in the <code>reverseFile</code> example, we’ve ignored the path entirely.</p><p>We <em>could</em> adjust our <code>MonadFS</code> instance to also track the path provided to each method, but this has a few problems. First, it means every test case would depend on all the various properties we are testing, which would mean updating every test case when we add a new one. It would also be simply impossible if we needed to track multiple types—in this particular case, it turns out that <code>String</code> and <code>FilePath</code> are actually the same type, but in practice, there may be a handful of disparate, incompatible types.</p><p>Both of the above issues could be fixed by creating a sum type and manually filtering out the relevant elements in each test case, but a much more intuitive approach would be to simply have a separate instance for each case. Unfortunately, in Haskell, creating a new instance means creating an entirely new type. To illustrate how much duplication that would entail, we could create the following type and instance for testing proper propagation of the <code>path</code> argument:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">])</span> + +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span></code></pre><p>Now it’s possible to add an extra test case that asserts that the proper path is provided to the two filesystem functions:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This works, but it’s ultimately unacceptably complicated. Our test harness code is now significantly larger than the actual tests themselves, and the amount of boilerplate is frustrating. Verbose test suites are especially bad, since forcing programmers to jump through hoops just to implement a single test reduces the likelihood that people will actually write good tests, if they write tests at all. In contrast, if writing tests is easy, then people will naturally write more of them.</p><p>The above strategy to writing tests is not good enough, but it does reveal a particular problem: in Haskell, typeclass instances are not first-class values that can be manipulated and abstracted over, they are static constructs that can only be managed by the compiler, and users do not have a direct way to modify them. With some cleverness, however, we can actually create an approximation of first-class typeclass dictionaries, which will allow us to dramatically simplify the above testing mechanism.</p><h2><a name="creating-first-class-typeclass-instances"></a>Creating first-class typeclass instances</h2><p>In order to provide an easy way to construct instances, we need a way to represent instances as ordinary Haskell values. This is not terribly difficult, given that instances are conceptually just records containing a collection of functions. For example, we could create a datatype that represents an instance of the <code>MonadFS</code> typeclass:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>To avoid namespace clashes with the actual method identifiers, the record fields are prefixed with an underscore, but otherwise, the translation is remarkably straightforward. Using this record type, we can easily create values that represent the two instances we defined above:</p><pre><code class="pygments"><span class="nf">contentInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> + +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>These two values represent two different implementations of <code>MonadFS</code>, but since they’re ordinary Haskell values, they can be manipulated and even <em>extended</em> like any other records. This can be extremely useful, since it makes it possible to create a sort of “base” instance, then have individual test cases override individual pieces of functionality piecemeal.</p><p>Of course, although we’ve written these two instances, we have no way to actually use them. After all, Haskell does not provide a way to explicitly provide typeclass dictionaries. Fortunately, we can create a sort of “proxy” type that will use a reader to thread the dictionary around explicitly, and the instance can defer to the dictionary’s implementation.</p><h3><a name="creating-an-instance-proxy"></a>Creating an instance proxy</h3><p>To represent our proxy type, we’ll use a combination of a <code>Writer</code> and a <code>ReaderT</code>; the former to implement the logging used by instances, and the latter to actually thread around the dictionary. Our type will look like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">log</span> +<span class="w"> </span><span class="p">)</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="n">inst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">inst</span><span class="p">)</span></code></pre><p>This might look rather complicated, and it is, but let’s break down exactly what it’s doing.</p><ol><li><p>The <code>TestM</code> type includes two type parameters. The first is the type of value that will be logged (hence the name <code>log</code>), which corresponds to the argument to <code>Writer</code> from previous incarnations of <code>TestM</code>. Unlike those types, though, we want this version to work with any <code>Monoid</code>, so we’ll make it a type parameter. The second parameter is simply the type of the current monadic value, as before.</p></li><li><p>The type itself is defined as a wrapper around a small monad transformer stack, the first of which is <code>ReaderT</code>. The state threaded around by the reader is, in this case, the instance dictionary, which is <code>MonadFSInst</code>.</p><p>However, recall that <code>MonadFSInst</code> accepts a type variable—the type of a monad itself—so we must provide <code>TestM log</code> as an argument to <code>MonadFSInst</code>. This slight bit of indirection allows us to tie the knot between the mutually dependent instances and proxy type.</p></li><li><p>The base monad in the transformer stack is <code>Writer</code>, which is used to actually implement the logging functionality, just like in prior cases. The only difference now is that the <code>log</code> type parameter now determines what the writer actually produces.</p></li><li><p>Finally, as before, we use <code>GeneralizedNewtypeDeriving</code> to derive all the relevant <code>mtl</code> classes, adding the somewhat wordy <code>MonadReader</code> constraint to the list.</p></li></ol><p>Using this single type, we can now implement a <code>MonadFS</code> instance that defers to the dictionary carried around within <code>TestM</code>’s reader state:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_readFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_writeFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This may seem somewhat boilerplate-y, and it is to some extent, but the important consideration is that this boilerplate only needs to be written <em>once</em>. With this in place, it’s now possible to write an arbitrary number of first-class instances that use the above mechanism without extending the mechanism at all.</p><p>To see what actually using this code would look like, let’s update the <code>reverseFile</code> tests to use the new <code>TestM</code> implementation, as well as the <code>contentInst</code> and <code>pathInst</code> dictionaries from earlier:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>We can do a little bit better, though. Really, the definitions of <code>contentInst</code> and <code>pathInst</code> are specific to each test case. With ordinary typeclass instances, we cannot scope them to any particular block, but since <code>MonadFSInst</code> is just an ordinary Haskell datatype, we can manipulate them just like any other Haskell values. Therefore, we can just inline those instances’ definitions into the test cases themselves to keep them closer to the actual tests.</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This is pretty good. We’re now able to create inline instances of our <code>MonadFS</code> typeclass, which allows us to write extremely concise unit tests using ordinary Haskell typeclasses as system seams. We’ve managed to cut down on the boilerplate considerably, though we still have a couple problems. For one, this example only uses a single typeclass containing only two methods. A real <code>MonadFS</code> typeclass would likely have at least a dozen methods for performing various filesystem operations, and writing out the instance dictionaries for every single method, even the ones that aren’t used within the code under test, would be pretty frustratingly verbose.</p><p>This problem is solvable, though. Since instances are just ordinary Haskell records, we can create a “base” instance that just throws an exception whenever the method is called:</p><pre><code class="pygments"><span class="nf">baseInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">baseInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_readFile’"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_writeFile’"</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Then code that only uses <code>readFile</code> could only override that particular method, for example:</p><pre><code class="pygments"><span class="kr">let</span><span class="w"> </span><span class="n">myInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">baseInst</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span></code></pre><p>Normally, of course, this would be a terrible idea. However, since this is all just test code, it can be extremely useful in quickly figuring out what methods need to be stubbed out for a particular test case. Since all the code actually gets run at test time, attempts to use unimplemented instance methods will immediately raise an error, informing the programmer which methods need to be implemented to make the test pass. This can also help to significantly cut down on the amount of effort it takes to implement each test.</p><p>Another problem is that our approach is specialized exclusively to <code>MonadFS</code>. What about functions that use both <code>MonadFS</code> <em>and</em> <code>MonadDB</code>, for example? Fortunately, that is not hard to solve, either. We can adapt the <code>MonadFSInst</code> type to include fields for all of the typeclasses relevant to our system, turning it into a generic test fixture of sorts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FixtureInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FixtureInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">-- MonadFS</span> +<span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="c1">-- MonadDB</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Updating <code>TestM</code> to use <code>FixtureInst</code> instead of <code>MonadFSInst</code> is trivial, and all the rest of the infrastructure still works. However, this means that every time a new typeclass is added, three things need to be updated:</p><ol><li><p>Its methods need to be added to the <code>FixtureInst</code> record.</p></li><li><p>Those methods need to be given error-raising defaults in the <code>baseInst</code> value.</p></li><li><p>An actual instance of the typeclass needs to be written for <code>TestM</code> that defers to the <code>FixtureInst</code> value.</p></li></ol><p>Furthermore, most of this manual manipulation of methods is required every time a particular typeclass changes, whether that means adding a method, removing a method, renaming a method, or changing a method’s type. This is especially frustrating given that all this code is really just mechanical boilerplate that could all be derived by the set of typeclasses being tested.</p><p>That last point is especially important: aside from the instances themselves, every piece of boilerplate above is obviously possible to generate from existing types alone. With that piece of information in mind, we can do even better: we can use Template Haskell.</p><h2><a name="removing-the-boilerplate-using-test-fixture"></a>Removing the boilerplate using <code>test-fixture</code></h2><p>The above code was not only rather boilerplate-heavy, it was pretty complicated. Fortunately, you don’t actually have to write it. Enter the library <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code></a>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture.TH</span> + +<span class="nf">mkFixture</span><span class="w"> </span><span class="s">"FixtureInst"</span><span class="w"> </span><span class="p">[</span><span class="kt">&#39;&#39;MonadFS</span><span class="p">,</span><span class="w"> </span><span class="kt">&#39;&#39;MonadDB</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">contentInst</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">pathInst</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p><strong>That’s it.</strong> The above code automatically generates everything you need to write fast, simple, deterministic unit tests in Haskell. The <code>mkFixture</code> function is a Template Haskell macro that expands into a definition quite similar to the <code>FixtureInst</code> type we wrote by hand, but since it’s automatically generated from the typeclass definitions, it never needs to be updated.</p><p>The <code>logTestFixture</code> function replaces the <code>logTestM</code> function we wrote by hand, but it works exactly the same. The <code>Control.Monad.TestFixture</code> library also exports a <code>log</code> function that is a synonym for <code>tell . singleton</code>, but using <code>tell</code> directly still works if you prefer.</p><p>The <code>mkFixture</code> function also generates a <code>Default</code> instance, which replaces the <code>baseInst</code> value defined earlier. It functions the same way, though, producing useful error messages that refer to the names of unimplemented typeclass methods that have not been stubbed out.</p><p>This blog post is not a <code>test-fixture</code> tutorial—indeed, it is much more complicated than a <code>test-fixture</code> tutorial would be, since it covers what the library is really doing under the hood—but if you’re interested, I would highly recommend you take a look at the <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code> documentation on Hackage</a>.</p><h2><a name="conclusion-credits-and-similar-techniques"></a>Conclusion, credits, and similar techniques</h2><p>This blog post came about as the result of a need my coworkers and I found when writing Haskell code; we wanted a way to write unit tests quickly and easily, but we didn’t find much advice from the rest of the Haskell ecosystem. The <code>test-fixture</code> library is the result of that exploratory work, and we currently use it to test a significant portion of our Haskell code.</p><p>It would be extremely unfair to suggest that I was the inventor of this technique or the inventor of the library. Two of my coworkers, <a href="https://github.com/jxv">Joe Vargas</a> and <a href="https://github.com/aztecrex">Greg Wiley</a>, came up with the general approach and wrote <code>Control.Monad.TestFixture</code>, and I simply wrote the Template Haskell macro to eliminate the boilerplate. With that in mind, I think I can say with some fairness that I think this technique is a joy to use when unit testing is a desirable goal, and I would definitely recommend it if you are interested in doing isolated testing in Haskell.</p><p>The general technique of using typeclasses to emulate effects was in part inspired by the well-known <code>mtl</code> library. An alternate approach to writing unit-testable Haskell code is using free monads, but overall, I prefer this approach over free monads because the typeclass constraints add type safety in ways that free monads do not (at least not without additional boilerplate), and this approach also lends itself well to static analysis-based boilerplate reduction techniques. It has its own tradeoffs, though, so if you’ve had success with free monads, then I certainly make no claim this is a superior approach, just one that I’ve personally found pleasant.</p><p>As a final note, if you <em>do</em> check out <code>test-fixture</code>, feel free to leave feedback by opening issues on <a href="https://github.com/cjdev/test-fixture/issues">the GitHub issue tracker</a>—even things like confusing documentation are worth a bug report.</p><ol class="footnotes"></ol></article>Climbing the infinite ladder of abstractionhttps://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/https://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/11 Aug 2016<article><p>I started programming in elementary school.</p><p>When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to <a href="https://xkcd.com/974/">solve the general problem</a>. When I learned about programming, I was immediately hooked: it was <em>so easy</em> to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.</p><p>Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of <strong>abstraction</strong>.</p><h2><a name="the-brick-wall-of-inexpressiveness"></a>The brick wall of inexpressiveness</h2><p>When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:</p><pre><code class="pygments"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="p">;</span> +<span class="p">}</span> + +<span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span> +<span class="p">}</span></code></pre><p>This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.</p><p>It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The <em>whole reason</em> I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.</p><p>When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.</p><p>Both of these languages were flawed, as all languages are, but they opened my mind to the idea that <em>programming languages</em> could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.</p><h2><a name="discovering-lisp"></a>Discovering Lisp</h2><p>Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.</p><p>This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?</p><p>The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a <a href="https://github.com/lexi-lambda/libsol">highly dynamic Lisp with a prototypal object system called Sol</a>. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.</p><p>Unfortunately, it was also abysmally slow.</p><p>I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called <a href="http://racket-lang.org">Racket</a>. At about the same time, someone pointed me to a totally different language called <a href="https://www.haskell.org">Haskell</a>. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.</p><h2><a name="a-journey-into-complexity"></a>A journey into complexity</h2><p>Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned <em>so much more</em> since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.</p><p>I’ve also started realizing something else, though: <strong>the languages I’ve settled into are <em>really complicated</em>.</strong></p><p>When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.</p><p>Why?</p><p>Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”</p><p>Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?</p><p>I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.</p><p>Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I <em>still</em> have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?</p><h3><a name="improvement-but-never-mastery"></a>Improvement, but never mastery</h3><p>Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.</p><p>For example I am pretty confident that I <em>get</em> JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques <em>within</em> the language, not entirely new linguistic constructs.</p><p>On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about <code>TypeInType</code>? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do <em>not</em> have a good grasp on all the intricacies of Racket’s macro system.</p><p>This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out <code>DataKinds</code>, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.</p><p>Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is <em>terrible</em>, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.</p><p>Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even <em>more</em> advanced things, and still, none of them truly solve my problems.</p><h2><a name="ultimately-it-all-has-at-least-a-little-value"></a>Ultimately, it all has (at least a little) value</h2><p>It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.</p><p>The scariest part of all is that I think it’s completely worthwhile.</p><p>So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs <em>seem</em> unnecessarily complicated, they <em>seem</em> increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.</p><p>The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.</p><p>Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:</p><pre><code class="pygments"><span class="nl">10</span><span class="w"> </span><span class="kr">INPUT</span><span class="w"> </span><span class="s2">"What is your name: "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span> +<span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">"Hello "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span></code></pre><p>On the other hand, very few would probably understand this one:</p><pre><code class="pygments"><span class="c1">-- | A class for categories.</span> +<span class="c1">-- id and (.) must form a monoid.</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">Category</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="c1">-- | the identity morphism</span> +<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="c1">-- | morphism composition</span> +<span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">c</span></code></pre><p>Yet very few new programs are being written in BASIC, and lots are being written in Haskell.</p><p>Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:</p><pre><code class="pygments"><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composeWithProps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">curry</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">childProps</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">omit</span><span class="p">([</span><span class="s1">&#39;children&#39;</span><span class="p">],</span><span class="w"> </span><span class="nx">childProps</span><span class="p">),</span><span class="w"> </span><span class="nx">childProps</span><span class="p">.</span><span class="nx">children</span><span class="p">));</span> +<span class="w"> </span><span class="c1">// give the composed component a pretty display name for debugging</span> +<span class="w"> </span><span class="nx">composed</span><span class="p">.</span><span class="nx">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`Composed(</span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="si">}</span><span class="sb">, </span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">composed</span><span class="p">;</span> +<span class="p">});</span></code></pre><p>Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.</p><p>That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an <em>extremely</em> productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.</p><h3><a name="reactionary-anti-intellectualism-and-the-search-for-moderation"></a>Reactionary anti-intellectualism and the search for moderation</h3><p>I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. <a href="https://twitter.com/lexi_lambda/status/763111451691134976">I recently tweeted a picture of some rather dense mathematics from a paper I’d read</a>, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.</p><p>Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like <code>map</code> should be banned because they are too confusing and not immediately self-explanatory.</p><p>I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.</p><p>Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern <em>technology</em> possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.</p><p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.</p><p>I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.</p><p>Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.</p><ol class="footnotes"></ol></article>Four months with Haskellhttps://lexi-lambda.github.io/blog/2016/06/12/four-months-with-haskell/https://lexi-lambda.github.io/blog/2016/06/12/four-months-with-haskell/12 Jun 2016<article><p>At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.</p><p>Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an <em>incredible</em> programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.</p><h2><a name="dispelling-some-myths"></a>Dispelling some myths</h2><p>Before moving on and discussing my experiences in depth, I want to take a quick detour to dispel some frequent rumors I hear about why Haskell is at least potentially problematic. These are things I hear a <em>lot</em>, and nothing in my experience so far would lead me to believe these are actually true. Ultimately, I don’t want to spend too much time on these—I think that, for the most part, they are nitpicks that people complain about to avoid understanding the deeper and more insidious problems with the language—but I think it’s important to at least mention them.</p><h3><a name="hiring-haskell-developers-is-not-hard"></a>Hiring Haskell developers is not hard</h3><p>I am on the first Haskell team in my company, and I am among the first Haskell developers we ever hired. Not only were we hiring without much experience with Haskell at all, we explicitly <em>did not</em> want to hire remote. Debate all you like about whether or not permitting remote work is a good idea, but I don’t think anyone would dispute that this constraint makes hiring much harder. We didn’t have any trouble finding a very large stream of qualified applicants, and it definitely seems to have dispelled any fears that we would have trouble finding new candidates in the future.</p><h3><a name="performing-i-o-in-haskell-is-easy"></a>Performing I/O in Haskell is easy</h3><p>Haskell’s purity is a point of real contention, and it’s one of the most frustrating complaints I often hear about Haskell. It is surprisingly common to hear concerns along the lines of “I don’t want to use Haskell because its academic devotion to purity sounds like it would make it very hard to get anything done”. There are very valid reasons to avoid Haskell, but in practice, I/O is not one of them. In fact, I found that isolating I/O in Haskell was much the same as isolating I/O in every other language, which I need to do anyway to permit unit testing.</p><p>...you <em>do</em> write deterministic unit tests for your impure logic, right?</p><h3><a name="working-with-lots-of-monads-is-not-very-difficult"></a>Working with lots of monads is not very difficult</h3><p>The “M word” has ended up being a running joke <em>about</em> Haskell that actually ends up coming up fairly rarely <em>within</em> the Haskell community. To be clear, there is <em>no doubt</em> in my mind that monads make Haskell intimidating and provide a steep learning curve for new users. The proliferation of the joke that monads are impossible to explain, to the point of becoming mythologized, is absolutely indicative of a deeper problem about Haskell’s accessibility. However, once people learn the basics about monads, I’ve found that applying them is just as natural as applying any other programming pattern.</p><p>Monads are used to assist the programmer, not impede them, and they really do pay off in practice. When something has a monadic interface, there’s a decent chance I already know what that interface is going to do, and that makes working with lots of different monads surprisingly easy. Admittedly, I do rely very, very heavily on tooling to help me out here, but with things like mouseover type tooltips, I’ve actually found that working with a variety of different monads and monad transformers is actually quite pleasant, and it makes things very readable!</p><h2><a name="haskell-the-good-parts"></a>Haskell: the good parts</h2><p>With the disclaimers out of the way, I really just want to gush for a little bit. This is not going to be an objective, reasoned survey of why Haskell is good. I am not even really going to touch upon why types are so great and why purity is so wonderful—I’d love to discuss those in depth, but that’s for a different blog post. For now, I just want to touch upon the real surprises, the real things that made me <em>excited</em> about Haskell in ways I didn’t expect. These are the things that my subjective little experience has found fun.</p><h3><a name="language-extensions-are-haskell"></a>Language extensions <em>are</em> Haskell</h3><p>There was a time in my life when I spent a lot of time writing C. There are a lot of compilers for C, and they all implement the language in subtly different but often incompatible ways, especially on different platforms. The only way to maintain a modicum of predictability was to adhere to the standards <em>religiously</em>, even when certain GCC or MSVC extensions seem tantalizingly useful. I was actually bitten a few times by real instances where I figured I’d just use a harmless extension that was implemented everywhere, then found out it worked slightly differently across different compilers in a particular edge case. It was a learning experience.</p><p>It seems that this fear provides a very real distrust for using GHC’s numerous <em>language extensions</em>, and indeed, for a long time, I felt that it was probably an admirable goal to stick to Haskell 98 or Haskell 2010 as closely as possible. Sometimes I chose a slightly more verbose solution that was standard Haskell to avoid turning on a trivial extension that would make the code look a little bit cleaner.</p><p>About a year later, I’m finding that attitude was not only a mistake, but it forced me to often completely miss out on a lot of Haskell’s core value. GHC <em>won</em>, and now GHC and Haskell are basically synonymous. With that in mind, the portability concerns of language extensions are a bit of a non-issue, and turning them on is a very good idea! Some extensions are more than a little dangerous, so they cannot all be turned on without thinking, but the question is absolutely not “Is using language extensions a good idea?” and more “Is using <em>this</em> language extension a good idea?”</p><p>This is important, and I bring it up for a reason: so much of the awesomeness of Haskell is locked behind language extensions. Turning a lot of these on is one of the main things that made me really start to see how incredibly powerful Haskell actually is.</p><h3><a name="phantom-types"></a>Phantom types</h3><p>I’m going to start out by talking about <em>phantom types</em>, which are a pretty simple concept but a powerful one, and they serve as the foundation for a lot of other cool type-level tricks that can make Haskell extremely interesting. The basic idea of a phantom type is simple; it’s a type parameter that isn’t actually used to represent any particular runtime value:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type represents an id for some kind of value, but although the kind of value is specified in the type as the <code>a</code> type parameter, it isn’t actually used anywhere on the data definition—no matter what <code>a</code> is, an <code>Id</code> is just a piece of text. This makes it possible to write functions that operate on specific kinds of ids, and those invariants will be statically checked by the compiler, even though the runtime representation is entirely identical:</p><pre><code class="pygments"><span class="nf">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span></code></pre><p>Using <code>FlexibleInstances</code>, it’s also possible to create different instances for different kinds of ids. For example, it would be possible to have different <code>Show</code> instances depending on the type of id in question.</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"user #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">txt</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"post #"</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">unpack</span><span class="w"> </span><span class="n">txt</span></code></pre><p>This provides a simple framework for encoding entirely arbitrary information into the type system, then asking the compiler to actually check assertions about that information. This is made even more powerful with some other extensions, which I’ll talk about shortly.</p><h3><a name="letting-the-compiler-write-code"></a>Letting the compiler write code</h3><p>One of the things I really dislike, more than most things, is boilerplate. A little bit of boilerplate is fine—even necessary at times—but as soon as I start wondering if a code generator would improve things, I think the programming language has pretty much failed me.</p><p>I write a lot of Racket because, in a sense, Racket is the ultimate boilerplate killer: the macro system is a first-class code generator integrated with the rest of the language, and it means that boilerplate is almost never an issue. Of course, that’s not always true: sometimes a bit of boilerplate <em>is</em> still necessary because macros cannot deduce enough information about the program to generate the code entirely on their own, and in Haskell, some of that information is actually present in the type system.</p><p>This leads to two absolutely incredible extensions, both of which are simple and related, but which actually <em>completely change</em> how I approach problems when programming. These two extensions are <code>GeneralizedNewtypeDeriving</code> and <code>StandaloneDeriving</code>.</p><h4><a name="newtypes-and-type-safety"></a>Newtypes and type safety</h4><p>The basic idea is that “newtypes” are just simple wrapper types in Haskell. This turns out to be extremely important when trying to find the value of Haskell because they allow you to harden type safety by specializing types to <em>your</em> domain. For example, consider a type representing a user’s name:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This type is extremely simple, and in fact isn’t even at all different from a simple <code>Text</code> value with respect to its representation, since all combinations of unicode characters are allowed in a name. Therefore, what’s the point of a separate type? Well, this allows Haskell to introduce actual compilation failures when two different kinds of textual data are mixed. This is not a new idea, and even in languages that don’t support this sort of thing, Joel Spolsky’s old blog post <a href="http://www.joelonsoftware.com/articles/Wrong.html">Making Wrong Code Look Wrong</a> describes how it can be done by convention. Still, almost every modern language makes this possible: in C, it would be a single-member <code>struct</code>, in class-based OO languages, it would be a single-member class... this is not a complicated idea.</p><p>The difference lies in its usage. In other languages, this strategy is actually not very frequently employed for the simple reason that it is almost always extremely annoying. You are forced to do tons of wrapping/unwrapping, and at that point it isn’t really clear if you’re even getting all that much value out of the distinction when your first solution to a type mismatch is wrapping or unwrapping the value without a second thought. In Haskell, however, this can be heavily mitigated by asking the compiler to <em>automatically derive typeclass implementations</em>, which allow the unwrapping/wrapping to effectively happen implicitly for a constrained set of operations.</p><h4><a name="using-generalizednewtypederiving"></a>Using <code>GeneralizedNewtypeDeriving</code></h4><p>Consider the <code>Name</code> type once again, but this time, let’s derive a class:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Name</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">IsString</span><span class="p">)</span></code></pre><p>The <code>IsString</code> typeclass in Haskell allows custom types to automatically be created from string literals. It is <em>not</em> handled specially by Haskell’s <code>deriving</code> mechanism. Since <code>Text</code> implements <code>IsString</code>, an instance will be generated that simply defers to the underlying type, automatically generating the code to wrap the result up in a <code>Name</code> box at the end. This means that code like this will now just magically work:</p><pre><code class="pygments"><span class="nf">name</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Name</span> +<span class="nf">name</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa P. Hacker"</span></code></pre><p>No boilerplate needs to be written! This is a neat trick, but it actually turns out to be far more useful than that simple example in practice. What really makes this functionality shine is when you want to derive <em>some</em> kinds of functionality but disallow some others. For example, using the <a href="https://hackage.haskell.org/package/text-conversions"><code>text-conversions</code></a> package, it’s possible to do something like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">ToText</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>This creates an opaque <code>Id</code> type, but it automatically generates conversions <em>to</em> textual formats. However, it does <em>not</em> automatically create <code>FromText</code> or <code>FromJSON</code> instances, which would be dangerous because decoding <code>Id</code>s can potentially fail. It’s then possible to write out those instances manually to preserve a type safety:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">FromText</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fromText</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="n">isValidId</span><span class="w"> </span><span class="n">str</span><span class="w"> </span><span class="kr">then</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">str</span><span class="p">)</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="p">(</span><span class="kt">String</span><span class="w"> </span><span class="n">val</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">fromText</span><span class="w"> </span><span class="n">val</span><span class="p">)</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="s">"invalid id"</span></code></pre><h4><a name="using-standalonederiving"></a>Using <code>StandaloneDeriving</code></h4><p>The ordinary <code>deriving</code> mechanism is extremely useful, especially when paired with the above, but sometimes it is desirable to have a little bit more flexibility. In these cases, <code>StandaloneDeriving</code> can help.</p><p>Take the <code>Id</code> example again: it has a phantom type, and simply adding something like <code>deriving (ToText)</code> with derive <code>ToText</code> instances for <em>all</em> kinds of ids. It is potentially useful, however, to derive instances for more specific id types. Using standalone <code>deriving</code> constructs permits this sort of flexibility.</p><pre><code class="pygments"><span class="kr">deriving</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ToText</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">Post</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toText</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">postIdToText</span></code></pre><p>This is an example where GHC language extensions end up becoming significantly more than the sum of their parts, which seems to be a fairly frequent realization. The <code>StandaloneDeriving</code> mechanism is a little bit useful without <code>GeneralizedNewtypeDeriving</code>, but when combined, they are incredibly powerful tools for getting a very fine-grained kind of type safety <em>without</em> writing any boilerplate.</p><h3><a name="datakinds-are-super-cool-with-caveats"></a>DataKinds are super cool, with caveats</h3><p>Phantom types are quite wonderful, but they can only encode <em>types</em>, not arbitrary data. That’s where <code>DataKinds</code> and <code>KindSignatures</code> come in: they allow lifting arbitrary datatypes to the type level so that things that would normally be purely runtime values can be used at compile-time as well.</p><p>The way this works is pretty simple—when you define a datatype, you also define a “datakind”:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Registered</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Anonymous</span></code></pre><p>Normally, the above declaration declares a <em>type</em>, <code>RegistrationStatus</code>, and two <em>data constructors</em>, <code>Registered</code> and <code>Anonymous</code>. With <code>DataKinds</code>, it also defines a <em>kind</em>, <code>RegistrationStatus</code>, and two <em>type constructors</em>, <code>Registered</code> and <code>Anonymous.</code></p><p>If that’s confusing, the way to understand that is to realize there is a sort of natural ordering here: types describe values, and kinds describe types. Therefore, turning on <code>DataKinds</code> “lifts” each definition by a single level, so types become kinds and values become types. This permits using these things at the type level:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">RegistrationStatus</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>In this example, <code>UserId</code> still has a single phantom type variable, <code>s</code>, but this time it is constrained to the <code>RegistrationStatus</code> kind. Therefore, it can <em>only</em> be <code>Registered</code> or <code>Anonymous</code>. This cooperates well with the aforementioned <code>StandaloneDeriving</code> mechanism, and it mostly provides a convenient way to constrain type variables to custom kinds.</p><p>In general, <code>DataKinds</code> is a much more powerful extension, allowing things like type-level natural numbers or strings, which can be used to perform actual type-level computation (especially in combination with <code>TypeFamilies</code>) or a sort of metaprogramming. In some cases, they can even be used to implement things emulating things you can do with dependent types.</p><p>I think <code>DataKinds</code> are a very cool Haskell extension, but there are a couple caveats. One of the main ones is how new kinds are defined: <code>DataKinds</code> “hijacks” the existing datatype declaration syntax by making every single datatype declaration define a type <em>and</em> a kind. This is a little confusing, and it would be nice if a different syntax was used so that each could be defined independently.</p><p>Similarly, it seems that a lot of work is being done to allow using runtime values at the type level, but I wonder if people will ever need to use, say, runtime values at the <em>kind</em> level. This immediately evokes thoughts of Racket’s phase-based macro system, and I wonder if some of this duplication would be unnecessary with something similar.</p><p>Food for thought, but overall, <code>DataKinds</code> are a very nice addition to help with precisely and specifically typing particular problems.</p><h3><a name="typeclasses-can-emulate-effects"></a>Typeclasses can emulate effects</h3><p>This is something that I’ve found interesting in my time writing Haskell because I have <em>no idea</em> if it’s idiomatic or not, but it seems pretty powerful. The initial motivator for this idea was figuring out how to test our code without constantly dropping into <code>IO</code>.</p><p>More generally, we wanted to be able to unit test by “mocking” out collaborators, as it would be described in object oriented programming. I was always semi-distrustful of mocking, and indeed, it seems likely that it is heavily abused in certain circles, but I’ve come to appreciate the need that sometimes it is important to stub things out, <em>even in pure code</em>.</p><p>As an example, consider some code that needs access to the current time. This is something that would normally require <code>IO</code>, but we likely want to be able to use the value in a pure context without “infecting” the entire program with <code>IO</code> types. In Haskell, I have generally seen three ways of handling this sort of thing:</p><ol><li><p>Just inject the required values into the function and produce them “higher up” where I/O is okay. If threading the value around becomes too burdensome, use a Reader monad.</p></li><li><p>Use a free monad or similar to create a pure DSL of sorts, then write interpreters for various implementations, one of which uses <code>IO</code>.</p></li><li><p>Create custom monadic typeclasses that provide interfaces to the functionality you want to perform, then create instances, one of which is an instance over <code>IO</code>.</p></li></ol><p>This last approach seems to be less common in Haskell, but it’s the approach we took, and it seems to work out remarkably well. Returning to the need to get the current time, we could pretty easily write such a typeclass to encode that need:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">UTCTime</span></code></pre><p>Now we can write functions that use the current time:</p><pre><code class="pygments"><span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span><span class="p">)</span></code></pre><p>Now, we can write instances for <code>CurrentTime</code> that will allow us to run the same code in different contexts:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runAppM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadIO</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">runTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">runTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="n">x</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">AppM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">liftIO</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Time</span><span class="o">.</span><span class="kt">Clock</span><span class="o">.</span><span class="n">getCurrentTime</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getCurrentTime</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">posixSecondsToUTCTime</span><span class="w"> </span><span class="mi">0</span></code></pre><p>Where this really starts to shine is when adding additional effects. For example, the above token validation function might also need information about some kind of secret used for signing. Under this model, it’s just another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">getTokenSecret</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Secret</span> + +<span class="nf">validateToken</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Token</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">validateToken</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">currentTime</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getCurrentTime</span> +<span class="w"> </span><span class="n">secret</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getTokenSecret</span> +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="p">(</span><span class="n">tokenExpirationDate</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">currentTime</span> +<span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">verifySignature</span><span class="w"> </span><span class="n">tok</span><span class="w"> </span><span class="n">secret</span><span class="p">)</span></code></pre><p>Of course, so far all of these functions have been extremely simple, and we’ve basically been using them as a glorified reader monad. In practice, though, we use this pattern for lots more than just retrieving values. For example, we might have a typeclass for database interactions:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">User</span><span class="p">)</span> +<span class="w"> </span><span class="n">insertUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">PersistenceError</span><span class="w"> </span><span class="p">(</span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="p">))</span></code></pre><p>With all of this done, it becomes incredibly easy to see which functions are using which effects:</p><pre><code class="pygments"><span class="nf">postUsers</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Persistence</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">TokenSecret</span><span class="w"> </span><span class="n">m</span><span class="p">)</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">postUsers</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">getHealthcheck</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">CurrentTime</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">Response</span> +<span class="nf">getHealthcheck</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>There’s no need to perform any lifting, and this all seems to scale quite nicely. We’ve written some additional utilities to help write tests against functions using these kinds of monadic interfaces, and even though there’s a little bit of annoying boilerplate in a few spots, overall it seems to work quite elegantly.</p><p>I’m not entirely sure how common this is in the Haskell community, but it’s certainly pretty neat how easy it is to get nearly all of the benefits of effect types in other languages simply by composing some of Haskell’s simplest features.</p><h3><a name="atom-s-ide-haskell-tooling-is-invaluable"></a>Atom’s ide-haskell tooling is invaluable</h3><p>Alright, so, confession time: I don’t use Emacs.</p><p>I know, I know, how is that possible? I write Lisp, after all. Well, honestly, I tried picking it up a number of times, but none of those times did I get far enough to ditch my other tools. For Racket work, I use DrRacket, but for almost everything else, I use Atom.</p><p>Atom has a lot of flaws, but it’s also pretty amazing in places, and I absolutely <em>love</em> the Haskell tooling written by the wonderful <a href="https://github.com/atom-haskell">atom-haskell</a> folks. I use it constantly, and even though it doesn’t always work perfectly, it works pretty well. When it has problems, I’ve at least figured out how to get it working correctly.</p><p>This is probably hard to really explain without seeing it for yourself, but I’ve found that I basically <em>depend</em> on this sort of tooling to be fully productive in Haskell, and I have no problem admitting that. The ability to get instant feedback about type errors tied to visual source locations, to be able to directly manipulate the source by selecting expressions and getting type information, and even the option to get inline linter suggestions means I spend a lot less time glancing at the terminal, and even less time in the REPL.</p><p>The tooling is far from perfect, and it leaves a lot to be desired in places (the idea of using that static information for automated, project-wide refactoring <em>a la</em> Java is tantalizing), but most of those things are ideas of what amazing things could be, not broken or missing essentials. I am pretty satisfied with ide-haskell right now, and I can only hope it continues to get better and better.</p><h2><a name="frustrations-drawbacks-and-pain-points"></a>Frustrations, drawbacks, and pain points</h2><p>Haskell is not perfect. In fact, far from it. There is a vast array of little annoyances that I have with the language, as is the case with any language. Still, there are a few overarching problems that I would really like to at least mention. These are the biggest sources of frustration for me so far.</p><h3><a name="purity-failure-and-exception-handling"></a>Purity, failure, and exception-handling</h3><p>One of Haskell’s defining features is its purity—I don’t think many would disagree with that. Some people consider it a drawback, others consider it one of its greatest boons. Personally, I like it a lot, and I think one of the best parts about it is how it requires the programmer to be incredibly deliberate about failure.</p><p>In many languages, when looking up a value from a container where the key doesn’t exist, there are really two ways to go about expressing this failure:</p><ol><li><p>Throw an exception.</p></li><li><p>Return <code>null</code>.</p></li></ol><p>The former is scary because it means <em>any</em> call to any function can make the entire program blow up, and it’s often impossible to know which functions even have the potential to throw. This creates a certain kind of non-local control flow that can sometimes cause a lot of unpredictability. The second option is much the same, especially when any value in a program might be <code>null</code>; it just defers the failure.</p><p>In languages with option types, this is somewhat mitigated. Java now has option types, too, but they are still frequently cumbersome to use because there is nothing like monads to use to simply chain operations together. Haskell, in comparison, has an incredible complement of tools to simply handle errors without a whole lot of burden on the programmer, and I have found that, in practice, this is <em>actually helpful</em> and I really do write better error-handling code.</p><h4><a name="first-the-good-parts"></a>First, the good parts</h4><p>I have seen a comparison drawn between throwing checked exceptions and returning <code>Maybe</code> or <code>Either</code> types, but in practice the difference is massive. Handling checked exceptions is a monotonous chore because they are not first-class values, they are actually entirely separate linguistic constructs. Consider a library that throws a <code>LibraryException</code>, and you want to wrap that library and convert those exceptions to <code>ApplicationException</code>s. Well, have fun writing this code dozens of times:</p><pre><code class="pygments"><span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomething</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span> + +<span class="c1">// ...</span> + +<span class="k">try</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">doSomethingElse</span><span class="p">();</span> +<span class="p">}</span><span class="w"> </span><span class="k">catch</span><span class="w"> </span><span class="p">(</span><span class="n">LibraryException</span><span class="w"> </span><span class="n">ex</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="n">ApplicationException</span><span class="p">.</span><span class="na">fromLibraryException</span><span class="p">(</span><span class="n">ex</span><span class="p">);</span> +<span class="p">}</span></code></pre><p>In Haskell, failure is just represented by first-class values, and it’s totally possible to write helper functions to abstract over that kind of boilerplate:</p><pre><code class="pygments"><span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">ApplicationError</span> +<span class="nf">libraryToApplication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span> + +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">LibraryError</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">ApplicationError</span><span class="w"> </span><span class="n">a</span> +<span class="nf">liftLibrary</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mapLeft</span><span class="w"> </span><span class="n">libraryToApplication</span></code></pre><p>Now, that same boilerplate-y code becomes nearly invisible:</p><pre><code class="pygments"><span class="nf">x</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomething</span> + +<span class="c1">-- ...</span> + +<span class="nf">y</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">liftLibrary</span><span class="w"> </span><span class="n">doSomethingElse</span></code></pre><p>This might not <em>seem</em> like much, but it really cuts down on the amount of visual noise, which ends up making all the difference. Boilerplate incurs a cost much bigger than simply taking the time to type it all out (though that’s important, too): the cognitive overhead of parsing which parts of a program are boilerplate has a significant impact on readability.</p><h4><a name="so-what-s-the-problem"></a>So what’s the problem?</h4><p>If error handling is so great in Haskell, then why am I putting it under the complaints section? Well, it turns out that not everyone seems to think it’s as great as I make it out to be because people seem to keep writing Haskell APIs that throw exceptions!</p><p>Despite what some purists would have you believe, Haskell has exceptions, and they are not uncommon. Lots of things can throw exceptions, some of which are probably reasonable. Failing to connect to a database is a pretty catastrophic error, so it seems fair that it would throw. On the other hand, inserting a duplicate record is pretty normal operation, so it seems like that should <em>not</em> throw.</p><p>I mostly treat exceptions in Haskell as unrecoverable catastrophes. If I throw an error in <em>my</em> code, I do not intend to catch it. That means something horrible happened, and I just want that horribleness to show up in a log somewhere so I can fix the problem. If I care about failure, there are better ways to handle that failure gracefully.</p><p>It’s also probably worth noting that exceptions in Haskell can be thrown from anywhere, even pure code, but can only be <em>caught</em> within the <code>IO</code> monad. This is especially scary, but I’ve seen it happen in actual libraries out in the wild, even ones that the entire Haskell ecosystem is built on. One of the crowning examples of this is the <code>text</code> package, which provides a function called <code>decodeUtf8</code> to convert bytestrings into text. Its type is very simple:</p><pre><code class="pygments"><span class="nf">decodeUtf8</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ByteString</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>But wait, what if the bytestring is not actually a valid UTF-8 string?</p><p>Boom. There goes the application.</p><p>Okay, okay, well, at least the <code>text</code> package provides another function, this one called <code>decodeUtf8'</code>, which returns an <code>Either</code>. This is good, and I’ve trained myself to only ever use <code>decodeUtf8'</code>, but it still has some pretty significant problems:</p><ul><li><p>The <em>safe</em> version of this function is the “prime” version, rather than the other way around, which encourages people to use the unsafe one. Ideally, the unsafe one should be explicitly labeled as such... maybe call it <code>unsafeDecodeUtf8</code>?</p></li><li><p>This is not a hypothetical problem. When using a Haskell JWT library, we found a function that converts a string into a JWT. Since not all strings are JWTs, the library intelligently returns a <code>Maybe</code>. Therefore, we figured we were safe.</p><p>A couple weeks later, we found that providing this function with invalid data was returning HTTP 500 errors. Why? Our error handling was meticulous! Well, the answer was a <code>decodeUtf8</code> call, hidden inside of the JWT library. This is especially egregious, given that the API it exposed returned a <code>Maybe</code> anyway! It would have been trivial to use the safe version there, instead, but the poor, misleading name led the library developer to overlook the bug lurking in the otherwise innocuous function.</p><p>Even worse, this function was totally pure, and we used it in pure code, so we could not simply wrap the function and catch the exception. We had two options: use <code>unsafePerformIO</code> (yuck!) or perform a check before handing the data to the buggy function. We chose the latter, but in some cases, I imagine that could be too difficult to do in order to make it feasible.</p></li></ul><p>The point I’m trying to make is that this is a real problem, and it seems to me that throwing exceptions invalidates one of the primary advantages of Haskell. It disappointed me to realize that a significant amount of code written by FP Complete, one of the primary authors of some of the most important “modern Haskell” code in existence (including Stack), seem to very frequently expose APIs that will throw.</p><p>I’m not sure how much of this stems from a fundamental divide in the Haskell ecosystem and how much it is simply due to Michael Snoyman’s coding style, given that he is the primary author of a number of these tools and libraries that seem very eager to throw exceptions. As just one example of a real situation in which we were surprised by this behavior, we used Snoyman’s http-client library and found that it mysteriously throws upon nearly <em>any</em> failure state:</p><blockquote><p>A note on exceptions: for the most part, all actions that perform I/O should be assumed to throw an <code>HttpException</code> in the event of some problem, and all pure functions will be total. For example, <code>withResponse</code>, <code>httpLbs</code>, and <code>BodyReader</code> can all throw exceptions.</p></blockquote><p>This doesn’t seem entirely unreasonable—after all, isn’t a failure to negotiate TLS fairly catastrophic?—until you consider our use case. We needed to make a subrequest during the extent of another HTTP request to our server, and if that subrequest fails, we absolutely need to handle that failure gracefully. Of course, this is not <em>terrible</em> given that we are in <code>IO</code> so we can actually catch these exceptions, but since this behavior was only noted in a single aside at the top of the documentation, we didn’t realize we were forgetting error handling until far too late and requests were silently failing.</p><p>Exceptions seem to devalue one of the most powerful concepts in Haskell: if I don’t consider all the possibilities, my code <em>does not compile</em>. In practice, when working with APIs that properly encode these possibilities into the type system, this value proposition seems to be real. I really do find myself writing code that works correctly as soon as it compiles. It’s almost magical.</p><p>Using exceptions throws that all out the window, and I wish the Haskell ecosystem was generally more cautious about when to use them.</p><h3><a name="the-string-problem"></a>The String problem</h3><p>I sort of alluded to this a tiny bit in the last section, and that is probably indicative of how bad this issue is. I’m just going to be blunt: <strong>In Haskell, strings suck.</strong></p><p>This is always a bit of an amusing point whenever it is discussed because of how silly it seems. Haskell is a research language with a cutting-edge type system and some of the fanciest features of any language in existence. When everyday programming might use things like “profunctors”, “injective type families”, and “generalized algebraic datatypes”, you would think that dealing with <em>strings</em> would be a well-solved problem.</p><p>But it isn’t. Haskell libraries frequently use not one, not two, but <em><strong>five</strong></em> kinds of strings. Let’s list them off, shall we?</p><ul><li><p>First off, there’s the built-in <code>String</code> type, which is actually an alias for the <code>[Char]</code> type. For those not intimately familiar with Haskell, that’s a <em>linked list of characters</em>. As <a href="http://www.stephendiehl.com/">Stephen Diehl</a> recently put it in <a href="http://www.stephendiehl.com/posts/strings.html">a blog post describing the disaster that is Haskell string types</a>:</p><blockquote><p>This is not only a bad representation, it’s quite possibly the least efficient (non-contrived) representation of text data possible and has horrible performance in both time and space. <em>And it’s used everywhere in Haskell.</em></p></blockquote><p>The point is, it’s really bad. This type is not a useful representation for textual data in practical applications.</p></li><li><p>Moving on, we have a fairly decent type, <code>Text</code>, which comes from <code>Data.Text</code> in the <code>text</code> package. This is a decent representation of text, and it’s probably the one that everything should use. Well, maybe. Because <code>Text</code> comes in two varieties: lazy and strict. Nobody seems to agree on which of those two should be used, though, and they are totally incompatible types: functions that work with one kind of text won’t work with the other. You have to manually convert between them.</p></li><li><p>Finally, we have <code>ByteString</code>, which is horribly misnamed because it really isn’t a string at all, at least not in the textual sense. A better name for this type would have simply been <code>Bytes</code>, which sounds a lot scarier. And that would be good, because data typed as a <code>ByteString</code> is as close as you can get in Haskell to not assigning a type at all: a bytestring holds arbitrary bytes without assigning them any meaning whatsoever!</p><p>Or at least, that’s the intention. The trouble is that people <em>don’t</em> treat bytestrings like that—they just use them to toss pieces of text around, even when those pieces of text have a well-defined encoding and represent textual data. This leads to the <code>decodeUtf8</code> problem mentioned above, but it’s bigger than that because it often ends up with some poor APIs that assign some interpretation to <code>ByteString</code> data without assigning it a different type.</p><p>Again, this is throwing away so much of Haskell’s safety. It would be like using <code>Int</code> to keep track of boolean data (“just use 0 and 1!”) or using empty and singleton lists instead of using <code>Maybe</code>. When you use the precise type, you encode invariants and contracts into statically-checked assertions, but when you use general types like <code>ByteString</code>, you give that up.</p><p>Oh, and did I mention that <code>ByteString</code>s also come in incompatible lazy and strict versions, too?</p></li></ul><p>So, obviously, the answer is to just stop using the bad types and to just use (one kind of) <code>Text</code> everywhere. Great! Except that the other types are totally inescapable. The entire standard library uses <code>String</code> exclusively—after all, <code>text</code> is a separate package—and small libraries often use <code>String</code> instead of <code>text</code> because they have no need to bring in the dependency. Of course, this just means every real application pays the performance hit of converting between all these different kinds of strings.</p><p>Similarly, those that <em>do</em> use <code>Text</code> often use different kinds of text, so code ends up littered with <code>fromStrict</code> or <code>toStrict</code> coercions, which (again) have a cost. I’ve already ranted enough about <code>ByteString</code>, but basically, if you’re using <code>ByteString</code> in your API to pass around data that is semantically text, you are causing me pain. Please stop.</p><p>It seems that the way <code>Data.Text</code> probably <em>should</em> have been designed was by making <code>Text</code> a typeclass, then making the lazy and strict implementations instances of that typeclass. Still, the fact that both of them exist would always cause problems. I’m actually unsure which one is the “correct” choice—I don’t know enough about how the two perform in practice—but it seems likely that picking <em>either</em> one would be a performance improvement over the current system, which is constantly spending time converting between the two.</p><p>This issue has been ranted about plenty, so I won’t ramble on, but if you’re designing new libraries, please, <em>please</em> use <code>Text</code>. Your users will thank you.</p><h3><a name="documentation-is-nearly-worthless"></a>Documentation is nearly worthless</h3><p>Finally, let’s talk about documentation.</p><p>One of my favorite programming languages is Racket. Racket has a documentation tool called Scribble. Scribble is special because it is a totally separate domain-specific language for writing documentation, and it makes it fun and easy to write good explanations. There are even forms for typesetting automatically-rendered examples that look like a REPL. If the examples ever break or become incorrect, the docs don’t even compile.</p><p>All of the Racket core library documentation makes sure to set a good example about what good documentation should look like. The vast majority of the documentation is paragraphs of prose and simple but practical examples. There are also type signatures (in the form of contracts), and those are super important, but they are so effective because of how the prose explains what each function does, when to use it, <em>why</em> you’d use it, and <em>why you wouldn’t use it</em>.</p><p>Everything is cross-referenced automatically. The documentation is completely searchable locally out of the box. As soon as you install a package, its docs are automatically indexed. User-written libraries tend to have pretty good docs, too, because the standard libraries set such a good example <em>and</em> because the tools are so fantastic. Racket docs are really nice, and they’re so good they actually make things like Stack Overflow or even Google mostly irrelevant. It’s all there in the manual.</p><p>Haskell documentation is the opposite of everything I just said.</p><ul><li><p>The core libraries are poorly documented. Most functions include a sentence of description, and almost none include examples. At their worst, the descriptions simply restate the type signature.</p></li><li><p>Third-party libraries’ documentation is even worse, going frequently completely undocumented and actually only including type signatures and nothing else.</p></li><li><p>Haddock is an incredibly user-hostile tool for writing anything other than tiny snippets of documentation and is not very good at supporting prose. Notably, Haddock’s documentation is not generated using Haddock (and it still manages to be almost unusable). Forcing all documentation into inline comments makes users unlikely to write much explanation, and there is no ability for abstraction.</p></li><li><p>Reading documentation locally is very difficult because there is no easy way to open documentation for a particular package in a web browser, and it’s <em>certainly</em> not searchable. This is especially ridiculous given that Hoogle exists, which is one of best ways to search API docs in existence. There should be a <code>stack hoogle</code> command that just opens a Hoogle page for all locally-installed packages and Just Works, but there isn’t.</p></li><li><p>Most valuable information exists outside of documentation, so Google becomes a go-to immediately after a quick glance at the docs, and information is spread across blog posts, mailing lists, and obscure reddit posts.</p></li></ul><p>This is a problem that cannot be fixed by just making Haddock better, nor can it be fixed simply by improving the existing standard library documentation. There is a fundamental problem with Haskell documentation (which, to be completely fair, is not unique to Haskell), which is that its tools do not support anything more than API docs.</p><p>Good documentation is so much more than “here’s what this function does”; it’s about guides and tutorials and case studies and common pitfalls. <a href="http://docs.racket-lang.org/lens/lens-guide.html">This is documentation for someone new to lenses.</a> <a href="https://hackage.haskell.org/package/lens#readme">This is not.</a> Take note of the difference.</p><h2><a name="conclusion-and-other-thoughts"></a>Conclusion and other thoughts</h2><p>Haskell is an incredible programming platform, and indeed, it is sometimes mind-boggling how complete it is. It also has a lot of rough edges, sometimes in places that feel like they need a lot more care, or perhaps they’re even simply unfinished.</p><p>I could spend weeks writing about all the things I really like or dislike about the language, discussing in fine detail all the things that have made me excited or all the little bits that have made me want to tear my hair out. Heck, I could probably spend a month writing about strings alone. That’s not the point, though... I took a risk with Haskell, and it’s paid off. I’m not yet sure exactly how I feel about it, or when I would chose it relative to other tools, but it is currently very high on my list of favorite technologies.</p><p>I did not come to Haskell with a distaste for static typing, despite the fact that I write so much Racket, a dynamically typed language (by default, at least). I don’t really use Typed Racket, and despite my love for Haskell and its type system, I am not sure I will use much more of it than I did before. Haskell and Racket are very different languages, which is justified in some places and probably sort of circumstantial in others.</p><p>The future of Haskell seems bright, and a lot of the changes in the just-released GHC 8 are extremely exciting. I did not list records as a pain point because the changes in GHC 8 appear to make them a <em>lot</em> more palatable, although whether or not they solve that problem completely remains to be seen. I will absolutely continue to write Haskell and push it to its limits where I can, and hopefully try and take as much as I can from it along the way.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/heroku.atom.xml b/feeds/heroku.atom.xml new file mode 100644 index 0000000..a2857b1 --- /dev/null +++ b/feeds/heroku.atom.xml @@ -0,0 +1,24 @@ +Posts tagged ‘heroku’ | Alexis King’s Blog2015-08-22T00:00:00ZDeploying Racket applications on Heroku2015-08-22T00:00:00Z2015-08-22T00:00:00ZAlexis King<article><p><a href="https://www.heroku.com">Heroku</a> is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.</p><h2><a name="building-the-server"></a>Building the server</h2><p>Racket's <a href="http://docs.racket-lang.org/web-server/index.html">web-server</a> package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">web-server/servlet</span> +<span class="w"> </span><span class="n">web-server/servlet-env</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="n">req</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">response/xexpr</span> +<span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="ss">html</span><span class="w"> </span><span class="p">(</span><span class="ss">head</span><span class="w"> </span><span class="p">(</span><span class="ss">title</span><span class="w"> </span><span class="s2">"Racket Heroku App"</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="ss">body</span><span class="w"> </span><span class="p">(</span><span class="ss">h1</span><span class="w"> </span><span class="s2">"It works!"</span><span class="p">)))))</span> + +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span></code></pre><p>Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the <code>PORT</code> environment variable. We can access this using the Racket <code>getenv</code>[racket] function.</p><p>Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass <code>#f</code> for the <code>#:listen-ip</code> argument.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;number</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">))</span> +<span class="w"> </span><span class="mi">8080</span><span class="p">))</span> +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span><span class="p">)</span></code></pre><p>Also, by default, <code>serve/servlet</code>[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.</p><pre><code class="pygments"><span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span> +<span class="w"> </span><span class="kd">#:command-line?</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span></code></pre><p>That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.</p><h2><a name="setting-up-our-app-for-heroku"></a>Setting up our app for Heroku</h2><p>The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:</p><pre><code class="pygments">$<span class="w"> </span>git<span class="w"> </span>init +$<span class="w"> </span>heroku<span class="w"> </span>git:remote<span class="w"> </span>-a<span class="w"> </span>racket-heroku-sample +$<span class="w"> </span>heroku<span class="w"> </span>buildpacks:set<span class="w"> </span>https://github.com/lexi-lambda/heroku-buildpack-racket</code></pre><p>We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the <code>RACKET_VERSION</code> environment variable as follows:</p><pre><code class="pygments">$<span class="w"> </span>heroku<span class="w"> </span>config:set<span class="w"> </span><span class="nv">RACKET_VERSION</span><span class="o">=</span><span class="m">6</span>.2.1</code></pre><p>Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.</p><p>Specifically, we just want to run our <code>serve.rkt</code> module. The Racket buildpack installs the repository as a package, so we can run <code>racket</code> with the <code>-l</code> flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:</p><pre><code>web: racket -l sample-heroku-app/server +</code></pre><p>Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can <a href="https://racket-heroku-sample.herokuapp.com">navigate to our app's URL and actually see it running live</a>!</p><h2><a name="conclusion"></a>Conclusion</h2><p>That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.</p><p>That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can <a href="https://github.com/lexi-lambda/racket-sample-heroku-app">find it on GitHub here</a>. For more details on the buildpack itself, <a href="https://github.com/lexi-lambda/heroku-buildpack-racket">it's also available on GitHub here</a>.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/heroku.rss.xml b/feeds/heroku.rss.xml new file mode 100644 index 0000000..0aca4b9 --- /dev/null +++ b/feeds/heroku.rss.xml @@ -0,0 +1,24 @@ +Posts tagged ‘heroku’ | Alexis King’s BlogPosts tagged ‘heroku’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/heroku.html22 Aug 201522 Aug 201560Deploying Racket applications on Herokuhttps://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/https://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/22 Aug 2015<article><p><a href="https://www.heroku.com">Heroku</a> is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.</p><h2><a name="building-the-server"></a>Building the server</h2><p>Racket's <a href="http://docs.racket-lang.org/web-server/index.html">web-server</a> package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">web-server/servlet</span> +<span class="w"> </span><span class="n">web-server/servlet-env</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="n">req</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">response/xexpr</span> +<span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="ss">html</span><span class="w"> </span><span class="p">(</span><span class="ss">head</span><span class="w"> </span><span class="p">(</span><span class="ss">title</span><span class="w"> </span><span class="s2">"Racket Heroku App"</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="ss">body</span><span class="w"> </span><span class="p">(</span><span class="ss">h1</span><span class="w"> </span><span class="s2">"It works!"</span><span class="p">)))))</span> + +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span></code></pre><p>Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the <code>PORT</code> environment variable. We can access this using the Racket <code>getenv</code>[racket] function.</p><p>Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass <code>#f</code> for the <code>#:listen-ip</code> argument.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;number</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">))</span> +<span class="w"> </span><span class="mi">8080</span><span class="p">))</span> +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span><span class="p">)</span></code></pre><p>Also, by default, <code>serve/servlet</code>[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.</p><pre><code class="pygments"><span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span> +<span class="w"> </span><span class="kd">#:command-line?</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span></code></pre><p>That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.</p><h2><a name="setting-up-our-app-for-heroku"></a>Setting up our app for Heroku</h2><p>The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:</p><pre><code class="pygments">$<span class="w"> </span>git<span class="w"> </span>init +$<span class="w"> </span>heroku<span class="w"> </span>git:remote<span class="w"> </span>-a<span class="w"> </span>racket-heroku-sample +$<span class="w"> </span>heroku<span class="w"> </span>buildpacks:set<span class="w"> </span>https://github.com/lexi-lambda/heroku-buildpack-racket</code></pre><p>We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the <code>RACKET_VERSION</code> environment variable as follows:</p><pre><code class="pygments">$<span class="w"> </span>heroku<span class="w"> </span>config:set<span class="w"> </span><span class="nv">RACKET_VERSION</span><span class="o">=</span><span class="m">6</span>.2.1</code></pre><p>Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.</p><p>Specifically, we just want to run our <code>serve.rkt</code> module. The Racket buildpack installs the repository as a package, so we can run <code>racket</code> with the <code>-l</code> flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:</p><pre><code>web: racket -l sample-heroku-app/server +</code></pre><p>Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can <a href="https://racket-heroku-sample.herokuapp.com">navigate to our app's URL and actually see it running live</a>!</p><h2><a name="conclusion"></a>Conclusion</h2><p>That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.</p><p>That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can <a href="https://github.com/lexi-lambda/racket-sample-heroku-app">find it on GitHub here</a>. For more details on the buildpack itself, <a href="https://github.com/lexi-lambda/heroku-buildpack-racket">it's also available on GitHub here</a>.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/javascript.atom.xml b/feeds/javascript.atom.xml new file mode 100644 index 0000000..7dcd82c --- /dev/null +++ b/feeds/javascript.atom.xml @@ -0,0 +1,48 @@ +Posts tagged ‘javascript’ | Alexis King’s Blog2016-08-24T00:00:00ZUnderstanding the npm dependency model2016-08-24T00:00:00Z2016-08-24T00:00:00ZAlexis King<article><p>Currently, <a href="https://www.npmjs.com">npm</a> is <em>the</em> package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like <a href="https://bower.io">Bower</a> are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects <strong>you</strong> as a user or package developer.</p><h2><a name="first-the-basics"></a>First, the basics</h2><p>At a high level, npm is not too dissimilar from other package managers for programming languages: packages depend on other packages, and they express those dependencies with <em>version ranges</em>. npm happens to use the <a href="http://semver.org">semver</a> versioning scheme to express those ranges, but the way it performs version resolution is mostly immaterial; what matters is that packages can depend on ranges rather than specific versions of packages.</p><p>This is rather important in any ecosystem, since locking a library to a specific set of dependencies could cause significant problems, but it’s actually much less of a problem in npm’s case compared to other, similar package systems. Indeed, it is often safe for a library author to pin a dependency to a specific version without affecting dependent packages or applications. The tricky bit is determining <em>when</em> this is safe and when it’s not, and this is what I so frequently find that people get wrong.</p><h2><a name="dependency-duplication-and-the-dependency-tree"></a>Dependency duplication and the dependency tree</h2><p>Most users of npm (or at least most package authors) eventually learn that, unlike other package managers, npm installs a <em>tree</em> of dependencies. That is, every package installed gets its own set of dependencies rather than forcing every package to share the same canonical set of packages. Obviously, virtually every single package manager in existence has to model a dependency tree at some point, since that’s how dependencies are expressed by programmers.</p><p>For example, consider two packages, <code>foo</code> and <code>bar</code>. Each of them have their own set of dependencies, which can be represented as a tree:</p><pre><code>foo +├── hello ^0.1.2 +└── world ^1.0.7 + +bar +├── hello ^0.2.8 +└── goodbye ^3.4.0 +</code></pre><p>Imagine an application that depends on <em>both</em> <code>foo</code> and <code>bar</code>. Obviously, the <code>world</code> and <code>goodbye</code> dependencies are totally unrelated, so how npm handles them is relatively uninteresting. However, consider the case of <code>hello</code>: both packages require conflicting versions.</p><p>Most package managers (including RubyGems/Bundler, pip, and Cabal) would simply barf here, reporting a version conflict. This is because, in most package management models, <strong>only one version of any particular package can be installed at a time</strong>. In that sense, one of the package manager’s primary responsibilities is to figure out a set of package versions that will satisfy every version constraint simultaneously.</p><p>In contrast, npm has a somewhat easier job: it’s totally okay with installing different versions of the same package because each package gets its own set of dependencies. In the aforementioned example, the resulting directory structure would look something like this:</p><pre><code>node_modules/ +├── foo/ +│ └── node_modules/ +│ ├── hello/ +│ └── world/ +└── bar/ + └── node_modules/ + ├── hello/ + └── goodbye/ +</code></pre><p>Notably, the directory structure very closely mirrors the actual dependency tree. The above diagram is something of a simplification: in practice, each transitive dependency would have its own <code>node_modules</code> directory and so on, but the directory structure can get pretty messy pretty quickly. (Furthermore, npm 3 performs some optimizations to attempt to share dependencies when it can, but those are ultimately unnecessary to actually understanding the model.)</p><p>This model is, of course, extremely simple. The obvious effect is that every package gets its own little sandbox, which works absolutely marvelously for utility libraries like <code>ramda</code>, <code>lodash</code>, or <code>underscore</code>. If <code>foo</code> depends on <code>ramda@^0.19.0</code> but <code>bar</code> depends on <code>ramda@^0.22.0</code>, they can both coexist completely peacefully without any problems.</p><p>At first blush, this system is <em>obviously</em> better than the alternative, flat model, so long as the underlying runtime supports the required module loading scheme. However, it is not without drawbacks.</p><p>The most apparent downside is a significant increase in code size, given the potential for many, many copies of the same package, all with different versions. An increase in code size can often mean more than just a larger program—it can have a significant impact on performance. Larger programs just don’t fit into CPU caches as easily, and merely having to page a program in and out can significantly slow things down. That’s mostly just a tradeoff, though, since you’re sacrificing performance, not program correctness.</p><p>The more insidious problem (and the one that I see crop up quite a lot in the npm ecosystem without much thought) is how dependency isolation can affect cross-package communication.</p><h2><a name="dependency-isolation-and-values-that-pass-package-boundaries"></a>Dependency isolation and values that pass package boundaries</h2><p>The earlier example of using <code>ramda</code> is a place where npm’s default dependency management scheme really shines, given that Ramda just provides a bunch of plain ol’ functions. Passing these around is totally harmless. In fact, mixing functions from two different versions of Ramda would be totally okay! Unfortunately, not all cases are nearly that simple.</p><p>Consider, for a moment, <code>react</code>. React components are very much <em>not</em> plain old data; they are complex values that can be extended, instantiated, and rendered in a variety of ways. React represents component structure and state using an internal, private format, using a mixture of carefully arranged keys and values and some of the more powerful features of JavaScript’s object system. This internal structure might very well change between React versions, so a React component defined with <code>react@0.3.0</code> likely won’t work quite right with <code>react@15.3.1</code>.</p><p>With that in mind, consider two packages that define their own React components and export them for consumers to use. Looking at their dependency tree, we might see something like this:</p><pre><code>awesome-button +└── react ^0.3.0 + +amazing-modal +└── react ^15.3.1 +</code></pre><p>Given that these two packages use wildly different versions of React, npm would give each of them their own copy of React, as requested, and packages would happily install. However, if you tried to use these components together, they wouldn’t work at all! A newer version of React simply cannot understand an old version’s component, so you would get a (likely confusing) runtime error.</p><p>What went wrong? Well, dependency isolation works great when a package’s dependencies are purely implementation details, never observable from outside of a package. However, as soon as a package’s dependency becomes exposed as part of its <em>interface</em>, dependency isolation is not only subtly wrong, it can cause complete failure at runtime. These are cases when traditional dependency management are much better—they will tell you as soon as you attempt to install two packages that they just don’t work together, rather than waiting for you to figure that out for yourself.</p><p>This might not sound <em>too</em> bad—after all, JavaScript is a very dynamic language, so static guarantees are mostly few and far between, and your tests should catch these problems should they arise—but it can cause unnecessary issues when two packages <em>can</em> theoretically work together fine, but because npm assigned each one its own copy of a particular package (that is, it wasn’t quite smart enough to figure out it could give them both the same copy), things break down.</p><p>Looking outside of npm specifically and considering this model when applied to other languages, it becomes increasingly clear that this won’t do. This blog post was inspired by <a href="https://www.reddit.com/r/haskell/comments/4zc6y3/why_doesnt_cabal_use_a_model_like_that_of_npm/?ref=share&amp;ref_source=link">a Reddit thread discussing the npm model applied to Haskell</a>, and this flaw was touted as a reason why it couldn’t possibly work for such a static language.</p><p>Due to the way the JavaScript ecosystem has evolved, it’s true that most people can often get away with this subtle potential for incorrect behavior without any problems. Specifically, JavaScript tends to rely on duck typing rather than more restrictive checks like <code>instanceof</code>, so objects that satisfy the same protocol will still be compatible, even if their implementations aren’t <em>quite</em> the same. However, npm actually provides a robust solution to this problem that allows package authors to explicitly express these “cross-interface” dependencies.</p><h3><a name="peer-dependencies"></a>Peer dependencies</h3><p>Normally, npm package dependencies are listed under a <code>"dependencies"</code> key in the package’s <code>package.json</code> file. There is, however, another, less-used key called <code>"peerDependencies"</code>, which has the same format as the ordinary dependencies list. The difference shows up in how npm performs dependency resolution: rather than getting its own copy of a peer dependency, a package expects that dependency to be provided by its dependent.</p><p>This effectively means that peer dependencies are effectively resolved using the “traditional” dependency resolution mechanism that tools like Bundler and Cabal use: there must be one canonical version that satisfies everyone’s constraint. Since npm 3, things are a little bit less straightforward (specifically, peer dependencies are not automatically installed unless a dependent package explicitly depends on the peer package itself), but the basic idea is the same. This means that package authors must make a choice for each dependency they install: should it be a normal dependency or a peer dependency?</p><p>This is where I think people tend to get a little lost, even those familiar with the peer dependency mechanism. Fortunately, the answer is relatively simple: is the dependency in question visible in <em>any place</em> in the package’s interface?</p><p>This is sometimes hard to see in JavaScript because the “types” are invisible; that is, they are dynamic and rarely explicitly written out. However, just because the types are dynamic does not mean they are not there at runtime (and in the heads of various programmers), so the rule still holds: if the type of a function in a package’s public interface somehow depends on a dependency, it should be a peer dependency.</p><p>To make this a little more concrete, let’s look at a couple of examples. First off, let’s take a look at some simple cases, starting with some uses of <code>ramda</code>:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">merge</span><span class="p">,</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;ramda&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">withDefaultConfig</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">config</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">merge</span><span class="p">({</span><span class="w"> </span><span class="nx">path</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;.&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">config</span><span class="p">)</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">add5</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">add</span><span class="p">(</span><span class="mf">5</span><span class="p">)</span></code></pre><p>The first example here is pretty obvious: in <code>withDefaultConfig</code>, <code>merge</code> is used purely as an implementation detail, so it’s safe, and it’s not part of the module’s interface. In <code>add5</code>, the example is a little trickier: the result of <code>add(5)</code> is a partially-applied function created by Ramda, so technically, a Ramda-created value is a part of this module’s interface. However, the contract <code>add5</code> has with the outside world is simply that it is a JavaScript function that adds five to its argument, and it doesn’t depend on any Ramda-specific functionality, so <code>ramda</code> can safely be a non-peer dependency.</p><p>Now let’s look at another example using the <code>jpeg</code> image library:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">Jpeg</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;jpeg&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createSquareBuffer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createSquareJpeg</span><span class="p">(</span><span class="nx">size</span><span class="p">).</span><span class="nx">encode</span><span class="p">(</span><span class="nx">cb</span><span class="p">)</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createSquareJpeg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">size</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Jpeg</span><span class="p">(</span><span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="nx">size</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="mf">0</span><span class="p">),</span><span class="w"> </span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="nx">size</span><span class="p">)</span></code></pre><p>In this case, the <code>createSquareBuffer</code> function invokes a callback with an ordinary Node.js <code>Buffer</code> object, so the <code>jpeg</code> library is an implementation detail. If that were the only function exposed by this module, <code>jpeg</code> could safely be a non-peer dependency. However, the <code>createSquareJpeg</code> function violates that rule: it returns a <code>Jpeg</code> object, which is an opaque value with a structure defined exclusively by the <code>jpeg</code> library. Therefore, a package with the above module <em>must</em> list <code>jpeg</code> as a peer dependency.</p><p>This sort of restriction works in reverse, too. For example, consider the following module:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">writeFile</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;fs&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">writeJpeg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span><span class="w"> </span><span class="nx">jpeg</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">jpeg</span><span class="p">.</span><span class="nx">encode</span><span class="p">((</span><span class="nx">image</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span><span class="w"> </span><span class="nx">image</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">))</span></code></pre><p>The above module does not even <em>import</em> the <code>jpeg</code> package, yet it implicitly depends on the <code>encode</code> method of the <code>Jpeg</code> interface. Therefore, despite not even explicitly using it anywhere in the code, a package containing the above module should include <code>jpeg</code> as a peer dependency.</p><p>They key is to carefully consider what contract your modules have with their dependents. If those contracts involve other packages in any way, they should be peer dependencies. If they don’t, they should be ordinary dependencies.</p><h2><a name="applying-the-npm-model-to-other-programming-languages"></a>Applying the npm model to other programming languages</h2><p>The npm model of package management is more complicated than that of other languages, but it provides a real advantage: implementation details are kept as implementation details. In other systems, it’s quite possible to find yourself in “dependency hell”, when you personally know that the version conflict reported by your package manager is not a real problem, but because the package system must pick a single canonical version, there’s no way to make progress without adjusting code in your dependencies. This is extremely frustrating.</p><p>This sort of dependency isolation is not the most advanced form of package management in existence—indeed, far from it—but it’s definitely more powerful than most other mainstream systems out there. Of course, most other languages could not adopt the npm model simply by changing the package manager: having a global package namespace can prevent multiple versions of the same package being installed at a <em>runtime</em> level. The reason npm is able to do what it does is because Node itself supports it.</p><p>That said, the dichotomy between peer and non-peer dependencies is a little confusing, especially to people who aren’t package authors. Figuring out which packages need to go in which group is not always obvious or trivial. Fortunately, other languages might be able to help.</p><p>Returning to Haskell, its strong static type system would potentially allow this distinction to be detected entirely automatically, and Cabal could actually report an error when a package used in an exposed interface was not listed as a peer dependency (much like how it currently prevents importing a transitive dependency without explicitly depending on it). This would allow helper function packages to keep on being implementation details while still maintaining strong interface safety. This would likely take a lot of work to get just right—managing the global nature of typeclass instances would likely make this much more complicated than a naïve approach would accommodate—but it would add a nice layer of flexibility that does not currently exist.</p><p>From the perspective of JavaScript, npm has demonstrated that it can be a capable package manager, despite the monumental burden placed upon it by the ever-growing, ever-changing JS ecosystem. As a package author myself, I would implore other users to carefully consider the peer dependencies feature and work hard to encode their interfaces’ contracts using it—it’s a commonly misunderstood gem of the npm model, and I hope this blog post helped to shed at least a little more light upon it.</p><ol class="footnotes"></ol></article>Canonical factories for testing with factory_girl_api2015-09-23T00:00:00Z2015-09-23T00:00:00ZAlexis King<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/javascript.rss.xml b/feeds/javascript.rss.xml new file mode 100644 index 0000000..4a640b2 --- /dev/null +++ b/feeds/javascript.rss.xml @@ -0,0 +1,48 @@ +Posts tagged ‘javascript’ | Alexis King’s BlogPosts tagged ‘javascript’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/javascript.html24 Aug 201624 Aug 201660Understanding the npm dependency modelhttps://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/https://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/24 Aug 2016<article><p>Currently, <a href="https://www.npmjs.com">npm</a> is <em>the</em> package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like <a href="https://bower.io">Bower</a> are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects <strong>you</strong> as a user or package developer.</p><h2><a name="first-the-basics"></a>First, the basics</h2><p>At a high level, npm is not too dissimilar from other package managers for programming languages: packages depend on other packages, and they express those dependencies with <em>version ranges</em>. npm happens to use the <a href="http://semver.org">semver</a> versioning scheme to express those ranges, but the way it performs version resolution is mostly immaterial; what matters is that packages can depend on ranges rather than specific versions of packages.</p><p>This is rather important in any ecosystem, since locking a library to a specific set of dependencies could cause significant problems, but it’s actually much less of a problem in npm’s case compared to other, similar package systems. Indeed, it is often safe for a library author to pin a dependency to a specific version without affecting dependent packages or applications. The tricky bit is determining <em>when</em> this is safe and when it’s not, and this is what I so frequently find that people get wrong.</p><h2><a name="dependency-duplication-and-the-dependency-tree"></a>Dependency duplication and the dependency tree</h2><p>Most users of npm (or at least most package authors) eventually learn that, unlike other package managers, npm installs a <em>tree</em> of dependencies. That is, every package installed gets its own set of dependencies rather than forcing every package to share the same canonical set of packages. Obviously, virtually every single package manager in existence has to model a dependency tree at some point, since that’s how dependencies are expressed by programmers.</p><p>For example, consider two packages, <code>foo</code> and <code>bar</code>. Each of them have their own set of dependencies, which can be represented as a tree:</p><pre><code>foo +├── hello ^0.1.2 +└── world ^1.0.7 + +bar +├── hello ^0.2.8 +└── goodbye ^3.4.0 +</code></pre><p>Imagine an application that depends on <em>both</em> <code>foo</code> and <code>bar</code>. Obviously, the <code>world</code> and <code>goodbye</code> dependencies are totally unrelated, so how npm handles them is relatively uninteresting. However, consider the case of <code>hello</code>: both packages require conflicting versions.</p><p>Most package managers (including RubyGems/Bundler, pip, and Cabal) would simply barf here, reporting a version conflict. This is because, in most package management models, <strong>only one version of any particular package can be installed at a time</strong>. In that sense, one of the package manager’s primary responsibilities is to figure out a set of package versions that will satisfy every version constraint simultaneously.</p><p>In contrast, npm has a somewhat easier job: it’s totally okay with installing different versions of the same package because each package gets its own set of dependencies. In the aforementioned example, the resulting directory structure would look something like this:</p><pre><code>node_modules/ +├── foo/ +│ └── node_modules/ +│ ├── hello/ +│ └── world/ +└── bar/ + └── node_modules/ + ├── hello/ + └── goodbye/ +</code></pre><p>Notably, the directory structure very closely mirrors the actual dependency tree. The above diagram is something of a simplification: in practice, each transitive dependency would have its own <code>node_modules</code> directory and so on, but the directory structure can get pretty messy pretty quickly. (Furthermore, npm 3 performs some optimizations to attempt to share dependencies when it can, but those are ultimately unnecessary to actually understanding the model.)</p><p>This model is, of course, extremely simple. The obvious effect is that every package gets its own little sandbox, which works absolutely marvelously for utility libraries like <code>ramda</code>, <code>lodash</code>, or <code>underscore</code>. If <code>foo</code> depends on <code>ramda@^0.19.0</code> but <code>bar</code> depends on <code>ramda@^0.22.0</code>, they can both coexist completely peacefully without any problems.</p><p>At first blush, this system is <em>obviously</em> better than the alternative, flat model, so long as the underlying runtime supports the required module loading scheme. However, it is not without drawbacks.</p><p>The most apparent downside is a significant increase in code size, given the potential for many, many copies of the same package, all with different versions. An increase in code size can often mean more than just a larger program—it can have a significant impact on performance. Larger programs just don’t fit into CPU caches as easily, and merely having to page a program in and out can significantly slow things down. That’s mostly just a tradeoff, though, since you’re sacrificing performance, not program correctness.</p><p>The more insidious problem (and the one that I see crop up quite a lot in the npm ecosystem without much thought) is how dependency isolation can affect cross-package communication.</p><h2><a name="dependency-isolation-and-values-that-pass-package-boundaries"></a>Dependency isolation and values that pass package boundaries</h2><p>The earlier example of using <code>ramda</code> is a place where npm’s default dependency management scheme really shines, given that Ramda just provides a bunch of plain ol’ functions. Passing these around is totally harmless. In fact, mixing functions from two different versions of Ramda would be totally okay! Unfortunately, not all cases are nearly that simple.</p><p>Consider, for a moment, <code>react</code>. React components are very much <em>not</em> plain old data; they are complex values that can be extended, instantiated, and rendered in a variety of ways. React represents component structure and state using an internal, private format, using a mixture of carefully arranged keys and values and some of the more powerful features of JavaScript’s object system. This internal structure might very well change between React versions, so a React component defined with <code>react@0.3.0</code> likely won’t work quite right with <code>react@15.3.1</code>.</p><p>With that in mind, consider two packages that define their own React components and export them for consumers to use. Looking at their dependency tree, we might see something like this:</p><pre><code>awesome-button +└── react ^0.3.0 + +amazing-modal +└── react ^15.3.1 +</code></pre><p>Given that these two packages use wildly different versions of React, npm would give each of them their own copy of React, as requested, and packages would happily install. However, if you tried to use these components together, they wouldn’t work at all! A newer version of React simply cannot understand an old version’s component, so you would get a (likely confusing) runtime error.</p><p>What went wrong? Well, dependency isolation works great when a package’s dependencies are purely implementation details, never observable from outside of a package. However, as soon as a package’s dependency becomes exposed as part of its <em>interface</em>, dependency isolation is not only subtly wrong, it can cause complete failure at runtime. These are cases when traditional dependency management are much better—they will tell you as soon as you attempt to install two packages that they just don’t work together, rather than waiting for you to figure that out for yourself.</p><p>This might not sound <em>too</em> bad—after all, JavaScript is a very dynamic language, so static guarantees are mostly few and far between, and your tests should catch these problems should they arise—but it can cause unnecessary issues when two packages <em>can</em> theoretically work together fine, but because npm assigned each one its own copy of a particular package (that is, it wasn’t quite smart enough to figure out it could give them both the same copy), things break down.</p><p>Looking outside of npm specifically and considering this model when applied to other languages, it becomes increasingly clear that this won’t do. This blog post was inspired by <a href="https://www.reddit.com/r/haskell/comments/4zc6y3/why_doesnt_cabal_use_a_model_like_that_of_npm/?ref=share&amp;ref_source=link">a Reddit thread discussing the npm model applied to Haskell</a>, and this flaw was touted as a reason why it couldn’t possibly work for such a static language.</p><p>Due to the way the JavaScript ecosystem has evolved, it’s true that most people can often get away with this subtle potential for incorrect behavior without any problems. Specifically, JavaScript tends to rely on duck typing rather than more restrictive checks like <code>instanceof</code>, so objects that satisfy the same protocol will still be compatible, even if their implementations aren’t <em>quite</em> the same. However, npm actually provides a robust solution to this problem that allows package authors to explicitly express these “cross-interface” dependencies.</p><h3><a name="peer-dependencies"></a>Peer dependencies</h3><p>Normally, npm package dependencies are listed under a <code>"dependencies"</code> key in the package’s <code>package.json</code> file. There is, however, another, less-used key called <code>"peerDependencies"</code>, which has the same format as the ordinary dependencies list. The difference shows up in how npm performs dependency resolution: rather than getting its own copy of a peer dependency, a package expects that dependency to be provided by its dependent.</p><p>This effectively means that peer dependencies are effectively resolved using the “traditional” dependency resolution mechanism that tools like Bundler and Cabal use: there must be one canonical version that satisfies everyone’s constraint. Since npm 3, things are a little bit less straightforward (specifically, peer dependencies are not automatically installed unless a dependent package explicitly depends on the peer package itself), but the basic idea is the same. This means that package authors must make a choice for each dependency they install: should it be a normal dependency or a peer dependency?</p><p>This is where I think people tend to get a little lost, even those familiar with the peer dependency mechanism. Fortunately, the answer is relatively simple: is the dependency in question visible in <em>any place</em> in the package’s interface?</p><p>This is sometimes hard to see in JavaScript because the “types” are invisible; that is, they are dynamic and rarely explicitly written out. However, just because the types are dynamic does not mean they are not there at runtime (and in the heads of various programmers), so the rule still holds: if the type of a function in a package’s public interface somehow depends on a dependency, it should be a peer dependency.</p><p>To make this a little more concrete, let’s look at a couple of examples. First off, let’s take a look at some simple cases, starting with some uses of <code>ramda</code>:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">merge</span><span class="p">,</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;ramda&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">withDefaultConfig</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">config</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">merge</span><span class="p">({</span><span class="w"> </span><span class="nx">path</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;.&#39;</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nx">config</span><span class="p">)</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">add5</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">add</span><span class="p">(</span><span class="mf">5</span><span class="p">)</span></code></pre><p>The first example here is pretty obvious: in <code>withDefaultConfig</code>, <code>merge</code> is used purely as an implementation detail, so it’s safe, and it’s not part of the module’s interface. In <code>add5</code>, the example is a little trickier: the result of <code>add(5)</code> is a partially-applied function created by Ramda, so technically, a Ramda-created value is a part of this module’s interface. However, the contract <code>add5</code> has with the outside world is simply that it is a JavaScript function that adds five to its argument, and it doesn’t depend on any Ramda-specific functionality, so <code>ramda</code> can safely be a non-peer dependency.</p><p>Now let’s look at another example using the <code>jpeg</code> image library:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">Jpeg</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;jpeg&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createSquareBuffer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createSquareJpeg</span><span class="p">(</span><span class="nx">size</span><span class="p">).</span><span class="nx">encode</span><span class="p">(</span><span class="nx">cb</span><span class="p">)</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">createSquareJpeg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">size</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Jpeg</span><span class="p">(</span><span class="nx">Buffer</span><span class="p">.</span><span class="nx">alloc</span><span class="p">(</span><span class="nx">size</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="mf">0</span><span class="p">),</span><span class="w"> </span><span class="nx">size</span><span class="p">,</span><span class="w"> </span><span class="nx">size</span><span class="p">)</span></code></pre><p>In this case, the <code>createSquareBuffer</code> function invokes a callback with an ordinary Node.js <code>Buffer</code> object, so the <code>jpeg</code> library is an implementation detail. If that were the only function exposed by this module, <code>jpeg</code> could safely be a non-peer dependency. However, the <code>createSquareJpeg</code> function violates that rule: it returns a <code>Jpeg</code> object, which is an opaque value with a structure defined exclusively by the <code>jpeg</code> library. Therefore, a package with the above module <em>must</em> list <code>jpeg</code> as a peer dependency.</p><p>This sort of restriction works in reverse, too. For example, consider the following module:</p><pre><code class="pygments"><span class="k">import</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">writeFile</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="kr">from</span><span class="w"> </span><span class="s1">&#39;fs&#39;</span> + +<span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">writeJpeg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span><span class="w"> </span><span class="nx">jpeg</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">jpeg</span><span class="p">.</span><span class="nx">encode</span><span class="p">((</span><span class="nx">image</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span><span class="w"> </span><span class="nx">image</span><span class="p">,</span><span class="w"> </span><span class="nx">cb</span><span class="p">))</span></code></pre><p>The above module does not even <em>import</em> the <code>jpeg</code> package, yet it implicitly depends on the <code>encode</code> method of the <code>Jpeg</code> interface. Therefore, despite not even explicitly using it anywhere in the code, a package containing the above module should include <code>jpeg</code> as a peer dependency.</p><p>They key is to carefully consider what contract your modules have with their dependents. If those contracts involve other packages in any way, they should be peer dependencies. If they don’t, they should be ordinary dependencies.</p><h2><a name="applying-the-npm-model-to-other-programming-languages"></a>Applying the npm model to other programming languages</h2><p>The npm model of package management is more complicated than that of other languages, but it provides a real advantage: implementation details are kept as implementation details. In other systems, it’s quite possible to find yourself in “dependency hell”, when you personally know that the version conflict reported by your package manager is not a real problem, but because the package system must pick a single canonical version, there’s no way to make progress without adjusting code in your dependencies. This is extremely frustrating.</p><p>This sort of dependency isolation is not the most advanced form of package management in existence—indeed, far from it—but it’s definitely more powerful than most other mainstream systems out there. Of course, most other languages could not adopt the npm model simply by changing the package manager: having a global package namespace can prevent multiple versions of the same package being installed at a <em>runtime</em> level. The reason npm is able to do what it does is because Node itself supports it.</p><p>That said, the dichotomy between peer and non-peer dependencies is a little confusing, especially to people who aren’t package authors. Figuring out which packages need to go in which group is not always obvious or trivial. Fortunately, other languages might be able to help.</p><p>Returning to Haskell, its strong static type system would potentially allow this distinction to be detected entirely automatically, and Cabal could actually report an error when a package used in an exposed interface was not listed as a peer dependency (much like how it currently prevents importing a transitive dependency without explicitly depending on it). This would allow helper function packages to keep on being implementation details while still maintaining strong interface safety. This would likely take a lot of work to get just right—managing the global nature of typeclass instances would likely make this much more complicated than a naïve approach would accommodate—but it would add a nice layer of flexibility that does not currently exist.</p><p>From the perspective of JavaScript, npm has demonstrated that it can be a capable package manager, despite the monumental burden placed upon it by the ever-growing, ever-changing JS ecosystem. As a package author myself, I would implore other users to carefully consider the peer dependencies feature and work hard to encode their interfaces’ contracts using it—it’s a commonly misunderstood gem of the npm model, and I hope this blog post helped to shed at least a little more light upon it.</p><ol class="footnotes"></ol></article>Canonical factories for testing with factory_girl_apihttps://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/https://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/23 Sep 2015<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/macros.atom.xml b/feeds/macros.atom.xml new file mode 100644 index 0000000..ebff648 --- /dev/null +++ b/feeds/macros.atom.xml @@ -0,0 +1,1315 @@ +Posts tagged ‘macros’ | Alexis King’s Blog2019-04-21T00:00:00ZDefeating Racket’s separate compilation guarantee2019-04-21T00:00:00Z2019-04-21T00:00:00ZAlexis King<article><p>Being a self-described <a href="https://felleisen.org/matthias/manifesto/sec_pl-pl.html">programming-language programming language</a> is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate <a href="https://www.cs.utah.edu/plt/publications/macromod.pdf">composable and compilable macros</a>. One of the module system’s foundational properties is its <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29"><em>separate compilation guarantee</em></a>, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is <em>essential</em> for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.</p><p>Yet today, in this blog post, we’re going to do exactly that.</p><h2><a name="what-is-the-separate-compilation-guarantee"></a>What is the separate compilation guarantee?</h2><p>Before we get to the fun part (i.e. breaking things), let’s go over some fundamentals so we understand what we’re breaking. The authoritative source for the separate compilation guarantee is <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">the Racket reference</a>, but it is dense, as authoritative sources tend to be. Although I enjoy reading technical manuals for sport, it is my understanding that not all the people who read this blog are as strange as I am, so let’s start with a quick primer, instead. (If you’re already an expert, feel free to <a href="#section:main-start">skip to the next section</a>.)</p><p>Racket is a macro-enabled programming language. In Racket, a macro is a user-defined, code-to-code transformation that occurs at compile-time. These transformations cannot make arbitrary changes to the program—in Racket, they are usually required to be <em>local</em>, affecting a single expression or definition at a time—but they may be implemented using arbitrary code. This means that a macro can, if it so desires, read the SSH keys off your filesystem and issue an HTTP request to send them someplace.</p><p>That kind of attack is bad, admittedly, but it’s also <em>uninteresting</em>: Racket allows you do all that and then some, making no attempt to prevent it.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Racket calls these “external effects,” things that affect state outside of the programming language. They sound scary, but in practice, <em>internal effects</em>—effects that mutate state inside the programming language—are a much bigger obstacle to practical programming. Let’s take a look at why.</p><p>Let’s say we have a module with some global, mutable state. Perhaps it is used to keep track of a set of delicious foods:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><p>Using this interface, let’s write a program that checks if a particular food, given as a command-line argument, is delicious:</p><pre><code class="pygments"><span class="c1">;; check-food.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"pineapple"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"sushi"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"cheesecake"</span><span class="p">)</span> + +<span class="p">(</span><span class="k">command-line</span> +<span class="w"> </span><span class="kd">#:args</span><span class="w"> </span><span class="p">[</span><span class="n">food-to-check</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is not delicious.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>cheesecake +cheesecake<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>licorice +licorice<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>Exhilarating. (Sorry, licorice fans.) But what if a <em>macro</em> were to call <code>add-delicious-food!</code>? What would happen? For example, what if we wrote a macro to add a lot of foods at once?<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="n">fst:string</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd:string</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">for*</span><span class="w"> </span><span class="p">([</span><span class="n">fst-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">fst</span><span class="w"> </span><span class="k">...</span><span class="p">]))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">snd</span><span class="w"> </span><span class="k">...</span><span class="p">]))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="p">(</span><span class="nb">string-append</span><span class="w"> </span><span class="n">fst-str</span><span class="w"> </span><span class="s2">" "</span><span class="w"> </span><span class="n">snd-str</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">))</span> + +<span class="c1">; should add “fried chicken,” “roasted chicken”, “fried potato,” and “roasted potato”</span> +<span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="s2">"fried"</span><span class="w"> </span><span class="s2">"roasted"</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="s2">"chicken"</span><span class="w"> </span><span class="s2">"potato"</span><span class="p">])</span></code></pre><p>Now, what do you think executing <code>racket check-food.rkt 'fried chicken'</code> will do?</p><p>Clearly, the program should print <code>fried chicken is a delicious food</code>, and indeed, many traditional Lisp systems would happily produce such a result. After all, running <code>racket check-food.rkt 'fried chicken'</code> must load the source code inside <code>check-food.rkt</code>, expand and compile it, then run the result. While the program is being expanded, the compile-time calls to <code>add-delicious-food!</code> should add new elements to the <code>delicious-food</code> set, so when the program is executed, the string <code>"fried chicken"</code> ought to be in it.</p><p>But if you actually try this yourself, you will find that <em>isn’t</em> what happens. Instead, Racket rejects the program:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +check-food.rkt:12:11:<span class="w"> </span>add-delicious-food!:<span class="w"> </span>reference<span class="w"> </span>to<span class="w"> </span>an<span class="w"> </span>unbound<span class="w"> </span>identifier +<span class="w"> </span>at<span class="w"> </span>phase:<span class="w"> </span><span class="m">1</span><span class="p">;</span><span class="w"> </span>the<span class="w"> </span>transformer<span class="w"> </span>environment +<span class="w"> </span><span class="k">in</span>:<span class="w"> </span>add-delicious-food!</code></pre><p>Why does Racket reject this program? Well, consider that Racket allows programs to be pre-compiled using <code>raco make</code>, doing all the work of macroexpansion and compilation to bytecode ahead of time. Subsequent runs of the program will use the pre-compiled version, without having to run all the macros again. This is a problem, since expanding the <code>add-food-combinations!</code> macro had side-effects that our program depended on!</p><p>If Racket allowed the above program, it might do different things depending on whether it was pre-compiled. Running directly from source code might treat <code>'fried chicken'</code> as a delicious food, while running from pre-compiled bytecode might not. Racket considers this unacceptable, so it disallows the program entirely.</p><h3><a name="preserving-separate-compilation-via-phases"></a>Preserving separate compilation via phases</h3><p>Hopefully, you are now mostly convinced that the above program is a bad one, but you might have some lingering doubts. You might, for example, wonder if Racket disallows mutable compile-time state entirely. That is not the case—Racket really does allow everything that happens at runtime to happen at compile-time—but it does prevent compile-time and run-time state from ever <em>interacting</em>. Racket stratifies every program into a compile-time part and a run-time part, and it restricts communication between them to limited, well-defined channels (mainly via expanding to code that does something at run-time).</p><p>Racket calls this system of stratification <em>phases</em>. Code that executes at run-time belongs to the <em>run-time phase</em>, while code that executes at compile-time (i.e. macros) belongs to the <em>compile-time phase</em>. When a variable is defined, it is always defined in a particular phase, so bindings declared with <code>define</code> can only be used at run-time, while bindings declared with <code>define-for-syntax</code> can only be used at compile-time. Since <code>add-delicious-food!</code> was declared using <code>define</code>, it was not allowed (and in fact was not even visible) in the body of the <code>add-food-combinations!</code> macro.</p><p>While the whole macro system could work precisely as just described, such a strict stratification would be incredibly rigid. Since every definition would belong to either run-time or compile-time, but never both, reusing run-time code to implement macros would be impossible. While the example in the previous section might make it seem like that’s a good thing, it very often isn’t: imagine if general-purpose functions like <code>map</code> and <code>filter</code> all needed to be written twice!</p><p>To avoid this problem, Racket allows modules to be imported at both run-time and compile-time, so long as it’s done explicitly. Writing <code>(require "some-library.rkt")</code> requires <code>some-library.rkt</code> for run-time code, but writing <code>(require (for-syntax "some-library.rkt"))</code> requires it for compile-time code. Requiring a module <code>for-syntax</code> is sort of like implicitly adjusting all of its uses of <code>define</code> to be <code>define-for-syntax</code>, instead, effectively shifting all the code from run-time to compile-time. This kind of operation is therefore known as <em>phase shifting</em> in Racket terminology.</p><p>We can use phase shifting to make the program we wrote compile. If we adjust the <code>require</code> at the beginning of our program, then we can ensure <code>add-delicious-food!</code> is visible to both the run-time and compile-time parts of <code>check-food.rkt</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">))</span></code></pre><p>Now our program compiles. However, if you’ve been following everything carefully, you should be wondering why! According to the last section, sharing state between run-time and compile-time fundamentally can’t work without introducing inconsistencies between uncompiled and pre-compiled code. And that’s true—such a thing would cause all sorts of problems, and Racket doesn’t allow it. If you run the program, whether pre-compiled or not, you’ll find it always does the same thing:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>This seems rather confusing. What happened to the calls to <code>add-delicious-food!</code> inside our <code>add-food-combinations!</code> macro? If we stick a <code>printf</code> inside <code>add-delicious-food!</code>, we’ll find that it really does get called:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"Registering ~a as a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And in fact, if we pre-compile <code>check-food.rkt</code>, we’ll see that the first four registrations appear at compile-time, exactly as we expect:</p><pre><code class="pygments">$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>The compile-time registrations really are happening, but Racket is automatically restricting the compile-time side-effects so they only apply at compile-time. After compilation has finished, Racket ensures that compile-time side effects are thrown away, and the run-time code starts over with fresh, untouched state. This guarantees consistent behavior, since it becomes impossible to distinguish at run-time whether a module was just compiled on the fly, or if it was pre-compiled long ago (possibly even on someone else’s computer).</p><p>This is the essence of the separate compilation guarantee. To summarize:</p><ul><li><p>Run-time and compile-time are distinct <em>phases</em> of execution, which cannot interact.</p></li><li><p>Modules can be required at multiple phases via <em>phase shifting</em>, but their state is kept separate. Each phase gets its own copy of the state.</p></li><li><p>Ensuring that the state is kept separate ensures predictable program behavior, no matter when the program is compiled.</p></li></ul><p>This summary is a simplification of phases in Racket. The full Racket module system does not have only two phases, since macros can also be <em>used</em> at compile-time to implement other macros, effectively creating a separate “compile-time” for the compile-time code. Each compile-time pass is isolated to its own phase, creating a finite but arbitrarily large number of distinct program phases (all but one of which occur at compile-time).</p><p>Furthermore, the separate compilation guarantee does not just isolate the state of each phase from the state of other phases but also isolates all compile-time state from the compile-time state of other modules. This ensures that compilation is still deterministic even if modules are compiled in a different <em>order</em>, or if several modules are sometimes compiled individually while other times compiled together all at once.</p><p>If you want to learn more, the full details of the module system are described at length in the <a href="https://docs.racket-lang.org/guide/phases.html">General Phase Levels</a> section of the Racket Guide, but the abridged summary I’ve described is enough for the purposes of this blog post. If the bulleted list above mostly made sense to you, you’re ready to move on.</p><h2 id="section:main-start">How we’re going to break it</h2><p>The separate compilation guarantee is a sturdy opponent, but it is not without weaknesses. Although no API in Racket, safe or unsafe, allows arbitrarily disabling phase separation, a couple features of Racket are already known to allow limited forms of cross-phase communication.</p><p>The most significant of these, and the one we’ll be using as our vector of attack, is the logger. Unlike many logging systems, which are exclusively string-oriented, Racket’s logging interface allows structured logging by associating an arbitrary Racket value with each and every log message. Since it is possible to set up listeners within Racket that receive log messages sent to a particular “topic,” the logger can be used as a communication channel to send values between different parts of a program.</p><p>The following program illustrates how this works. One thread creates a listener for all log messages on the topic <code>'send-me-a-value</code> using <code>make-log-receiver</code>, then uses <code>sync</code> to block until a value is received. Meanwhile, a second thread sends values through the logger using <code>log-message</code>. Together, this creates a makeshift buffered, asynchronous channel:</p><pre><code class="pygments"><span class="c1">;; log-comm.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span><span class="w"> </span><span class="c1">; wait forever</span></code></pre><pre><code>$ racket log-comm.rkt +'#(debug "" 1 send-me-a-value) +'#(debug "" 2 send-me-a-value) +'#(debug "" 3 send-me-a-value) +'#(debug "" 4 send-me-a-value) +^Cuser break +</code></pre><p>In this program, the value being sent through the logger is just a number, which isn’t very interesting. But the value really can be <em>any</em> value, even arbitrary closures or mutable data structures. It’s even possible to send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> through a logger, which can subsequently be used to communicate directly, without having to abuse the logger.</p><p>Generally, this feature of loggers isn’t very useful, since Racket has plenty of features for cross-thread communication. What’s special about the logger, however, is that it is global, and it is cross-phase.</p><p>The cross-phase nature of the logger makes some sense. If a Racket program creates a namespace (that is, a fresh environment for dynamic evaluation), then uses it to expand and compile a Racket module, the process of compilation might produce some log messages, and the calling thread might wish to receive them. It wouldn’t be a very useful logging system if log messages during compile-time were always lost. However, this convenience is a loophole in the phase separation system, since it allows values to flow—bidirectionally—between phases.</p><p>This concept forms the foundation of our exploit, but it alone is not a new technique, and I did not discover it. However, all existing uses I know of that use the logger for cross-phase communication require control of the parent namespace in which modules are being compiled, which means some code must exist “outside” the actual program. That technique does not work for ordinary programs run directly with <code>racket</code> or compiled directly with <code>raco make</code>, so to get there, we’ll need something more clever.</p><h3><a name="the-challenge"></a>The challenge</h3><p>Our goal, therefore, is to share state between phases <em>without</em> controlling the compilation namespace. More precisely, we want to be able to create an arbitrary module-level definition that is <em>cross-phase persistent</em>, which means it will be evaluated once and only once no matter how many times its enclosing module is re-instantiated (i.e. given a fresh, untouched state) at various phases. A phase-shifted <code>require</code> of the module that contains the definition should share state with an unshifted version of the module, breaking the separate compilation guarantee wide open.</p><p>To use the example from the previous section, we should be able to adjust <code>foods.rkt</code> very slightly…</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="c1">; share across phases</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="cm">#| ... |#</span></code></pre><p>…and the <code>delicious-foods</code> mutable state should magically become cross-phase persistent. When running <code>check-food.rkt</code> from source, we should see the side-effects persisted from the module’s compilation, while running from pre-compiled bytecode should give us the result with compile-time effects discarded.</p><p>We already know the logger is going to be part of our exploit, but implementing <code>define/cross-phase</code> on top of it is more subtle than it might seem. In our previous example that used <code>make-log-receiver</code>, we had well-defined sender and receiver threads, but who is the “sender” in our multi-phased world? And what exactly is the sender sending?</p><p>To answer those questions, allow me to outline the general idea of our approach:</p><ol><li><p>The first time our <code>foods.rkt</code> module is instantiated, at any phase, it evaluates the <code>(mutable-set)</code> expression to produce a new mutable set. It spawns a sender thread that sends this value via the logger to anyone who will listen, and that thread lingers in the background for the remaining duration of the program.</p></li><li><p>All subsequent instantiations of <code>foods.rkt</code> do <em>not</em> evaluate the <code>(mutable-set)</code> expression. Instead, they obtain the existing set by creating a log receiver and obtaining the value the sender thread is broadcasting. This ensures that a single value is shared across all instantiations of the module.</p></li></ol><p>This sounds deceptively simple, but the crux of the problem is how to determine whether <code>foods.rkt</code> has previously been instantiated or not. Since we can only communicate across phases via the logger, we cannot use any shared state to directly record the first time the module is instantiated. We can listen to a log receiver and wait to see if we get a response, but this introduces a race condition: how long do we wait until giving up and deciding we’re the first instantiation? Worse, what if two threads instantiate the module at the same time, and both threads end up spawning a new sender thread, duplicating the state?</p><p>The true challenge, therefore, is to develop a protocol by which we can be <em>certain</em> we are the first instantiation of a module, without relying on any unspecified behavior, and without introducing any race conditions. This is possible, but it isn’t obvious, and it requires combining loggers with some extra tools available to the Racket programmer.</p><h3><a name="the-key-idea"></a>The key idea</h3><p>It’s finally time to tackle the key idea at the heart of our exploit: garbage collection. In Racket, garbage collection is an observable effect, since Racket allows attaching finalizers to values via <a href="https://docs.racket-lang.org/reference/willexecutor.html">wills and executors</a>. Since a single heap is necessarily shared by the entire VM, behavior happening on other threads (even in other phases) can be indirectly observed by creating a unique value—a “canary”—then sending it to another thread, and waiting to see if it will be garbage collected or not (that is, whether or not the canary “dies”).</p><p>Remember that logs and log receivers are effectively buffered, multicast, asynchronous FIFO channels. Since they are buffered, if any thread is already listening to a logger topic when a value is sent, it cannot possibly be garbage collected until that thread either reads it and discards it or the receiver itself is garbage collected. It’s possible to use this mechanism to observe whether or not another thread is already listening on a topic, as the following program demonstrates:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">executor</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><pre><code>$ racket check-receivers.rkt +no receivers for 'bar +receiver exists for 'foo +</code></pre><p>However, this program has some problems. For one, it needs to call <code>collect-garbage</code> several times to be certain that the canary will be collected if there are no listeners, which can take a second or two, and it also assumes that three calls to <code>collect-garbage</code> will be enough to collect the canary, though there is no guarantee that will be true.</p><p>A bulletproof solution should be both reasonably performant and guaranteed to work. To get there, we have to combine this idea with something more. Here’s the trick: instead of sending the canary alone, send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> alongside it. Synchronize on both the canary’s executor <em>and</em> the channel so that the thread will unblock if either the canary is collected <em>or</em> the channel is received and sent a value using <code>channel-put</code>. Have the receiver listen for the channel on a separate thread, and when it receives it, send a value back to unblock the waiting thread as quickly as possible, without needing to rely on a timeout or a particular number of calls to <code>collect-garbage</code>.</p><p>Using that idea, we can revise the program:</p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="c1">; send the channel + the canary</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">canary</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span><span class="w"> </span><span class="c1">; yield to try to let the receiver thread work</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">received</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">)))</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">received</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span><span class="w"> </span><span class="c1">; collect garbage and try again</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> +<span class="p">(</span><span class="nb">void</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="k">_</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><p>Now the program completes almost instantly. For this simple program, the explicit <code>(sleep)</code> call is effective enough at yielding that, on my machine, <code>(check-receivers 'foo)</code> returns without ever calling <code>collect-garbage</code>, and <code>(check-receivers 'bar)</code> returns after performing a single minor collection.</p><p>This is extremely close to a bulletproof solution, but there are two remaining subtle issues:</p><ol><li><p>There is technically a race condition between the <code>(sync recv)</code> in the receiver thread and the subsequent <code>channel-put</code>, since it’s possible for the canary to be received, discarded, and garbage collected before reaching the call to <code>channel-put</code>, which the sending thread would incorrectly interpret as indicating the topic has no receivers.</p><p>To fix that, the receiver thread can send the canary itself back through the channel, which fundamentally has to work, since the value cannot be collected until it has been received by the sending thread, at which point the <code>sync</code> has already chosen the channel.</p></li><li><p>It is possible for the receiver thread to receive the log message and call <code>channel-put</code>, but for the sending thread to somehow die in the meantime (which cannot be protected against in general in Racket, since <code>thread-kill</code> immediately and forcibly terminates a thread). If this were to happen, the sending thread would never obtain the value from the channel, blocking the receiving thread indefinitely.</p><p>A solution is to spawn a new thread for each <code>channel-put</code> instead of calling it directly from the receiving thread. Conveniently, this both ensures the receiving thread never gets stuck and avoids resource leaks, since the Racket runtime is smart enough to GC a thread blocked on a channel that has no other references and therefore can never be unblocked.</p></li></ol><p>With those fixes in place, the program is, to the best of my knowledge, bulletproof. It will always correctly determine whether or not a logger has a listener, with no race conditions or reliance upon unspecified behavior of the Racket runtime. It does, however, make a couple of assumptions.</p><p>First, it assumes that the value of <code>(current-logger)</code> is shared between the threads. It is true that <code>(current-logger)</code> can be changed, and sometimes is, but it’s usually done via <code>parameterize</code>, not mutation of the parameter directly. Therefore, this can largely be mitigated by storing the value of <code>(current-logger)</code> at module instantiation time.</p><p>Second, it assumes that no other receivers are listening on the same topic. Technically, even using a unique, uninterned key for the topic is insufficient to ensure that no receivers are listening to it, since a receiver can choose to listen to all topics. However, in practice, it is highly unlikely that any receiver would willfully choose to listen to all topics at the <code>'debug</code> level, since the receiver would be inundated with enormous amounts of useless information. Even if such a receiver were to be created, it is highly likely that it would dequeue the messages as quickly as possible and discard the accompanying payload, since doing otherwise would cause all messages to be retained in memory, leading to a significant memory leak.</p><p>Both these problems can be mitigated by using a logger other than the root logger, which is easy in this example. However, for the purpose of subverting the separate compilation guarantee, we would have no way to share the logger object itself across phases, defeating the whole purpose, so we are forced to use the root logger and hope the above two assumptions remain true (but they usually do).</p><h3><a name="preparing-the-exploit"></a>Preparing the exploit</h3><p>If you’ve made it here, congratulations! The most difficult part of this blog post is over. All that’s left is the fun part: performing the exploit.</p><p>The bulk of our implementation is a slightly adapted version of <code>check-receivers</code>:</p><pre><code class="pygments"><span class="c1">;; define-cross-phase.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="k">thunk</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="k">==</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="nb">eq?</span><span class="p">))</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">execute-evt</span><span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="nb">will-execute</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">execute-evt</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">result</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">value</span><span class="p">)</span> +<span class="w"> </span><span class="n">value</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="p">(</span><span class="k">thunk</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">value</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)]))))</span> +<span class="w"> </span><span class="n">value</span><span class="p">]))</span></code></pre><p>There are a few minor differences, which I’ll list:</p><ol><li><p>The most obvious difference is that <code>make-cross-phase</code> does the work of both checking if a receiver exists—which I’ll call the <em>manager thread</em>—and spawning it if it doesn’t. If it does end up spawning a manager thread, it evaluates the given thunk to produce a value, which becomes the cross-phase value that will be sent through the channel alongside the canary.</p></li><li><p>Once the manager thread is created, subsequent calls to <code>make-cross-phase</code> will receive the value through the channel and return it instead of re-invoking <code>thunk</code>. This is what ensures the right-hand side of each use of <code>define/cross-phase</code> is only ever evaluated once.</p></li><li><p>Since <code>make-cross-phase</code> needs to create a log receiver if no manager thread exists, it does so immediately, before sending the canary through the logger. This avoids a race condition between multiple threads that are simultaneously competing to become the manager thread, where both threads could send a canary through the logger before either was listening, both canaries would get GC’d, and both threads would spawn a new manager.<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><p>Creating the receiver before sending the canary avoids this problem, but the thread now needs to receive its own canary and discard it before synchronizing on the channel and executor, since otherwise it will retain a reference to the canary. It’s possible that in between creating the receiver and sending the canary, another thread also sent a canary, so it needs to drop any log messages it finds that don’t include its own canary.</p><p>This ends up working out perfectly, since every thread drops all the messages received before the one containing its own canary, but retains all subsequent values. This means that only one thread can ever “win” and become the manager, since the first thread to send a canary is guaranteed to retain all subsequent canaries, yet also guaranteed its canary will be GC’d. Other threads racing to become the manager will remain blocked until the manager thread is created, since its canaries will be retained by the manager-to-be until it dequeues them.</p><p>(This is the most subtle part of the process to get right, but conveniently, it mostly just works out without very much code. If you didn’t understand any of the above three paragraphs, it isn’t a big deal.)</p></li></ol><p>The final piece to this puzzle is to define the <code>define/cross-phase</code> macro that wraps <code>make-cross-phase</code>. The macro is actually slightly more involved than just generating a call to <code>make-cross-phase</code> directly, since we’d like to use an uninterned symbol for the topic instead of an interned one, just to ensure it is unique. Ordinarily, this might seem impossible, since an uninterned symbol is fundamentally a unique value that needs to be communicated across phases, and the whole problem we are solving is creating a communication channel that spans phases. However, Racket actually provides some built-in support for sharing uninterned symbols across phases (plus some other kinds of values, but they must always be immutable). To do this, we need to generate a <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._cross-phase._persistent-modules%29">cross-phase persistent submodule</a> that exports an uninterned symbol, then pass that symbol as the topic to <code>make-cross-phase</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">define/cross-phase</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">cross-phase-topic-key</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">#%kernel</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%declare</span><span class="w"> </span><span class="kd">#:cross-phase-persistent</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%provide</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">topic</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="s2">"cross-phase"</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">topic-mod-name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">e</span><span class="p">)))))</span></code></pre><p>And that’s really it. We’re done.</p><h3><a name="executing-the-exploit"></a>Executing the exploit</h3><p>With our implementation of <code>define/cross-phase</code> in hand, all that’s left to do is run our original <code>check-foods.rkt</code> program and see what happens:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +set-add!:<span class="w"> </span>contract<span class="w"> </span>violation: +expected:<span class="w"> </span>set? +given:<span class="w"> </span><span class="o">(</span>mutable-set<span class="w"> </span><span class="s2">"fried chicken"</span><span class="w"> </span><span class="s2">"roasted chicken"</span><span class="w"> </span><span class="s2">"roasted potato"</span><span class="w"> </span><span class="s2">"fried potato"</span><span class="o">)</span> +argument<span class="w"> </span>position:<span class="w"> </span>1st +other<span class="w"> </span>arguments...: +<span class="w"> </span>x:<span class="w"> </span><span class="s2">"pineapple"</span></code></pre><p>Well, I don’t know what you expected. Play stupid games, win stupid prizes.</p><p>This error actually makes sense, but it belies one reason (of many) why this whole endeavor is probably a bad idea. Although we’ve managed to make our mutable set cross-phase persistent, our references to set operations like <code>set-add!</code> and <code>set-member?</code> are not, and every time <code>racket/set</code> is instantiated in a fresh phase, it creates an entirely new instance of the <code>set</code> structure type. This means that even though we have a bona fide mutable set, it isn’t actually the type of set that this phase’s <code>set-add!</code> understands!</p><p>Of course, this isn’t a problem that some liberal application of <code>define/cross-phase</code> can’t solve:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-member?</span><span class="w"> </span><span class="nb">set-member?</span><span class="p">)</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-add!</span><span class="w"> </span><span class="nb">set-add!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And thus we find that another so-called “guarantee” isn’t.</p><h2><a name="reflection"></a>Reflection</h2><p>Now comes the time in the blog post when I have to step back and think about what I’ve done. Have mercy.</p><p>Everything in this blog post is a terrible idea. No, you should not use loggers for anything that isn’t logging, you shouldn’t use wills and executors for critical control flow, and obviously you should absolutely not intentionally break one of the most helpful guarantees the Racket module system affords you.</p><p>But I thought it was fun to do all that, anyway.</p><p>The meaningful takeaways from this blog post aren’t that the separate compilation guarantee can be broken, nor that any of the particular techniques I used hold, but that</p><ol><li><p>ensuring non-trivial guarantees is really hard,</p></li><li><p>despite that, the separate compilation guarantee is really, really hard to break,</p></li><li><p>the separate compilation guarantee is good, and you should appreciate the luxury it affords you while writing Racket macros,</p></li><li><p>avoiding races in a concurrent environment can be extremely subtle,</p></li><li><p>and Racket is totally <em>awesome</em> for giving me this much rope to hang myself with.</p></li></ol><p>If you want to hang yourself with Racket, too, <a href="https://gist.github.com/lexi-lambda/f173a84fc9727977bcea657b3bb0cd4f">runnable code from this blog post is available here</a>.</p><ol class="footnotes"><li id="footnote-1"><p>This isn’t <em>strictly</em> true, since Racket provides sandboxing mechanisms that can compile and execute untrusted code without file system or network access, but this is not the default compilation mode. Usually, it doesn’t matter nearly as much as it might sound: most of the time, if you’re compiling untrusted code, you’re also going to run it, and running untrusted code can do all those things, anyway. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>This is actually a <em>terrible</em> use case for a macro, since an ordinary function would do just fine, but I’m simplifying a little to keep the example small. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Racket actually provides this functionality directly via the <code>log-level?</code> procedure. However, since <code>log-level?</code> provides no way to determine how <em>many</em> receivers are listening to a topic, using it to guard against creating a receiver is vulnerable to a race condition that the garbage collection-based approach can avoid, as is discussed later. Furthermore, the GC technique is more likely to be resilient to nosy log receivers listening on all topics at the <code>'debug</code> level, since they will almost certainly dequeue and discard the value quickly (as otherwise they would leak large quantities of memory). <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>This race is the one that makes using <code>log-level?</code> untenable, since the receiver needs to be created before the topic is checked for listeners to avoid the race, which can’t be done with <code>log-level?</code> (since it would always return <code>#t</code>). <a href="#footnote-ref-4-1">↩</a></p></li></ol></article>Macroexpand anywhere with local-apply-transformer!2018-10-06T00:00:00Z2018-10-06T00:00:00ZAlexis King<article><p>Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only <em>certain positions</em> in Racket code are subject to macroexpansion.</p><p>To illustrate, consider a macro that provides a Clojure-style <code>let</code> form:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This can be used anywhere an expression is expected, and it does as one would expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>However, a novice macro programmer might realize that <code>clj-let</code> really only modifies the syntax of <em>binding pairs</em> for a <code>let</code> form. Therefore, could one define a macro that only adjusts the binding pairs of some existing <code>let</code> form instead of expanding to an entire <code>let</code>? That is, could one write the above example like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>The answer is <em>no</em>: the binding pairs of a <code>let</code> form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion <em>anywhere</em> in a Racket program.</p><h2><a name="why-only-some-positions-are-subject-to-macroexpansion"></a>Why only some positions are subject to macroexpansion</h2><p>To understand <em>why</em> the macroexpander refuses to touch certain positions in a program, we must first understand how the macro system operates. In Racket, a macro is defined as a compile-time function associated with a particular binding, and macros are given complete control over the syntax trees they are surrounded with. If we define a macro <em><code>mac</code></em>, then we write the expression <code>(<em>mac</em> <em>form</em>)</code>, <em><code>form</code></em> is provided as-is to <em><code>mac</code></em> as a syntax object. Its structure can be anything at all, since <em><code>mac</code></em> can be an arbitrary Racket function, and that function can use <em><code>form</code></em> however it pleases.</p><p>To give a concrete illustration, consider a macro that binds some identifiers to symbols in a local scope:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">x</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="ss">hello</span><span class="w"> </span><span class="ss">goodbye</span><span class="p">)</span></code></pre><p>It isn’t the most exciting macro in the world, but it illustrates a key point: the first subform to <code>let-symbols</code> is a list of identifiers that are eventually put in <em>binding</em> position. This means that <code>hello</code> and <code>goodbye</code> are bindings, not uses, and such bindings shadow any existing bindings that might have been in scope:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">foo</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> +<span class="w"> </span><span class="n">foo</span><span class="p">))</span> +<span class="o">&#39;</span><span class="ss">foo</span></code></pre><p>This might not seem very interesting, but it’s critical to understand, since it means that the expander <em>can’t know</em> which sub-pieces of a use of <code>let-symbols</code> will eventually be expressions themselves until it expands the macro and discovers it produces a <code>let</code> form, so it can’t know where it’s safe to perform macroexpansion. To make this more explicit, imagine we define a macro under some name, then try and use that name with our <code>let-symbols</code> macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">x:id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="n">hello</span><span class="p">)</span></code></pre><p>What should the above program do? If we treat the first use of <code>hello</code> in the <code>let-symbols</code> form as a macro application, then <code>(hello goodbye)</code> should be transformed into <code>(goodbye)</code>, and the use of <code>hello</code> in the body should be a syntax error. But if the first use of <code>hello</code> was instead intended to be a binder, then it should shadow the <code>hello</code> definition above, and the output of the program should be <code>'hello</code>.</p><p>To avoid the chaos that would ensue if defining a macro could completely break local reasoning about other macros, Racket chooses the second option, and the program produces <code>'hello</code>. The macroexpander has no way of knowing <em>how</em> each macro will inspect its constituent pieces, so it avoids touching anything until the macro expands. After it discovers the <code>let</code> form in the expansion of <code>let-symbols</code>, it can safely determine that the body expressions are, indeed, expressions, and it can recursively expand any macros they contain. To put things another way, a macro’s sub-forms are never expanded before the macro itself is expanded, only after.</p><h2><a name="forcing-sub-form-expansion"></a>Forcing sub-form expansion</h2><p>The above section explains why the expander must operate as it does, but it’s a little bit unsatisfying. What if we write a macro where we <em>want</em> certain sub-forms to be expanded before they are passed to us? Fortunately, the Racket macro system provides an API to handle this use case, too.</p><p>It is true that the Racket macro system never <em>automatically</em> expands sub-forms before outer forms are expanded, but macro transformers can explicitly op-in to recursive expansion via the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a> function. This function effectively yields control back to the expander to expand some arbitrary piece of syntax as an expression, and when it returns, the macro transformer can inspect the expanded expression however it wishes. In theory, this can be used to implement extensible macros that allow macroexpansion in locations other than expression position.</p><p>To give an example of such a macro, consider the Racket <code>match</code> form, which implements an expressive pattern-matcher as a macro. One of the most interesting qualities of Racket’s <code>match</code> macro is that its pattern language is user-extensible, essentially allowing pattern-level macros. For example, a user might find they frequently match against natural numbers, and they wish to be able to write <code>(nat n)</code> as a shorthand for <code>(? exact-nonnegative-integer? n)</code>. Fortunately, this is easy using <code>define-match-expander</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-match-expander</span><span class="w"> </span><span class="n">nat</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">pat</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">?</span><span class="w"> </span><span class="nb">exact-nonnegative-integer?</span><span class="w"> </span><span class="n">pat</span><span class="p">)]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="mi">-5</span><span class="w"> </span><span class="mi">-2</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">-7</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">list</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">nat</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">n</span><span class="p">])</span> +<span class="mi">4</span></code></pre><p>Clearly, <code>match</code> is somehow expanding the <code>nat</code> match expander as a part of its expansion. Is it using <code>local-expand</code>?</p><p>Well, no. While <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">a previous blog post of mine</a> has illustrated that it is possible to do such a thing with <code>local-expand</code> via some clever trickery, <code>local-expand</code> is really designed to expand <em>expressions</em>. This is a problem, since <code>(nat n)</code> is not an expression, it’s a pattern: it will expand into <code>(? exact-nonnegative-integer? n)</code>, which will lead to a syntax error, since <code>?</code> is not bound in the world of expressions. Instead, for a long while, <code>match</code> and forms like it have emulated how the expander performs macroexpansion in ad-hoc ways. Fortunately, as of Racket v7.0, the new <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Fapply-transformer..rkt%29._local-apply-transformer%29%29"><code>local-apply-transformer</code></a> API provides a way to invoke recursive macroexpansion in a consistent way, and it doesn’t assume that what’s being expanded is an expression.</p><h3><a name="a-closer-look-at-local-apply-transformer"></a>A closer look at <code>local-apply-transformer</code></h3><p>If <code>local-apply-transformer</code> is the answer, what does it actually do? Well, <code>local-apply-transformer</code> allows explicitly invoking a transformer function on some piece of syntax and retrieving the result. In other words, <code>local-apply-transformer</code> allows expanding an arbitrary macro, but since it doesn’t make any assumptions about what the output will be, it only expands it <em>once</em>: just a single step of macro transformation.</p><p>To illustrate, we can write a macro that uses <code>local-apply-transformer</code> to invoke a transformer function and preserve the result using <code>quote-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/apply-transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define-for-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>When we use <code>mac</code>, our <code>flip</code> function will be applied, as a macro, to the syntax object we provide:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="nb">&gt;</span></code></pre><p>Alright, so this works, but it raises some questions. Why is <code>flip</code> defined as a function at phase 1 (using <code>define-for-syntax</code>) instead of as a macro (using <code>define-syntax</code>)? What’s the deal with the <code>'expression</code> argument to <code>local-apply-transformer</code> given that <code>local-apply-transformer</code> is supposedly decoupled from expression expansion? And finally, how is this any different from just calling our <code>flip</code> function on the syntax object directly by writing <code>(flip #'(([x 1]) let x))</code>?</p><p>Let’s start with the first of those questions: why is <code>flip</code> defined as a function rather than as a macro? Well, <code>local-apply-transformer</code> is a fairly low-level operation: remember, it doesn’t assume <em>anything</em> about the argument it’s given! Therefore, it doesn’t take an expression containing a macro and expand it based on its structure, it needs to be explicitly provided the macro transformer function to apply. In practice, this might not seem very useful, since presumably we want to write our macros as macros, not as phase 1 functions. Fortunately, it’s possible to look up the function associated with a macro binding using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, so if we use that, we can define <code>flip</code> using <code>define-syntax</code> as usual:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>Now for the next question: what is the meaning of the <code>'expression</code> argument? This one is more of a historical artifact than anything else: when the expander applies a macro transformer, it does it in a “context”, which is accessible using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-context%29%29"><code>syntax-local-context</code></a> function. This context can be one of a predefined enumeration of cases, including <code>'expression</code>, <code>'top-level</code>, <code>'module</code>, <code>'module-begin</code>, or a list representing a definition context. Whether or not any of those actually apply to our use case, we still have to pick one, but aside from how they affect the value returned by <code>syntax-local-context</code> (which some macros inspect), the value we choose is largely irrelevant. Using <code>'expression</code> will do, even if it’s a bit of a lie.</p><p>Finally, how does any of this differ from just applying the function we get directly? Well, the critical answer is all about <em>hygiene</em>. Racket’s macro system is hygienic, which, among other things, ensures bindings defined with the same name in different places do not unintentionally conflict. Racket’s hygiene mechanism is implemented in the macroexpander, when macro transformers are applied. If we just applied the <code>flip</code> transformer procedure to a syntax object directly, we would circumvent this hygiene mechanism, potentially causing all sorts of problems. By using <code>local-apply-transformer</code>, we ensure hygiene is preserved.</p><p>There is one small problem left with our program, however. Can you spot it? The key is to consider what would happen if we used <code>flip</code> as an ordinary macro, without using <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="n">let:</span><span class="w"> </span><span class="n">bad</span><span class="w"> </span><span class="k">syntax</span> +<span class="w"> </span><span class="n">in:</span><span class="w"> </span><span class="k">let</span></code></pre><p>What happened? Well, remember that when a macro in Racket is used, it receives the whole use site as a syntax object: in this case, <code>#'(flip (([x 1]) let x))</code>. This means that <code>flip</code> ought to be written to parse its argument slightly differently:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span></code></pre><p>Indeed, now that we’ve properly restructured the macro, we can easily switch to using the convenient <code>define-simple-macro</code> shorthand:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This means we also need to update our definition of <code>mac</code> to provide the full syntax object the expander would:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>This might seem redundant, but remember, <code>local-apply-transformer</code> is very low-level! While the convention that <code>(<em>mac</em> . _)</code> is the syntax for a macro transformation might seem obvious, <code>local-apply-transformer</code> makes no assumptions. It just does what we tell it to do.</p><h3><a name="applying-local-apply-transformer"></a>Applying <code>local-apply-transformer</code></h3><p>So what does <code>local-apply-transformer</code> have to do with the problem at the beginning of this blog post? Well, as it happens, we can use <code>local-apply-transformer</code> to implement a macro that allows expansion <em>anywhere</em> using some simple tricks. While it’s true that we cannot magically divine which locations ought to be expanded, what we <em>can</em> do is explicitly annotate which places to expand.</p><p>To do this, we will implement a macro, <code>expand-inside</code>, that looks for subforms annotated with a special <code>$expand</code> identifier and performs macro transformation on those locations before proceeding with ordinary macroexpansion. Using the <code>clj-binding-pairs</code> example from the beginning of this blog post, our solution to that problem will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Put another way, <code>expand-inside</code> will force eager expansion on any subform surrounded with an <code>$expand</code> annotation.</p><p>We’ll start by defining the <code>$expand</code> binding itself. This binding won’t mean anything at all outside of <code>expand-inside</code>, but we’d like it to be a unique binding so that users can rename it (using, <code>rename-in</code>, for example) if they wish. To do this, we’ll use the usual trick of defining it as a macro that always produces an error if it’s ever used:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"illegal outside an ‘expand-inside’ form"</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span></code></pre><p>Next, we’ll implement a syntax class that will form the bulk of our implementation of <code>expand-inside</code>. Since we need to find uses of <code>$expand</code> that might be deeply-nested inside the syntax object provided to <code>expand-inside</code>, we need to recursively look through the syntax object, find any instances of <code>$expand</code>, and put it all back together once we’re done. This can be done relatively cleanly using a recursive syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">do-expand-inside</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="n">$expand</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">$expand</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:do-expand-inside</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">a:do-expand-inside</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">b:do-expand-inside</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">reassembled</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">a.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">reassembled</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">reassembled</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>There are some tricky details to get right in the reassembly of pairs, since syntax lists are actually composed of ordinary pairs rather than syntax pairs, but ultimately, the code for walking a syntax object is small. The key case of this syntax class is the call to <code>do-$expand</code> in the first clause, which we have not yet defined. This function will actually handle performing the expansion by invoking <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">trans</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}})</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">static</span><span class="w"> </span><span class="p">(</span><span class="nb">disjoin</span><span class="w"> </span><span class="nb">procedure?</span><span class="w"> </span><span class="nb">set!-transformer?</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"syntax transformer"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">trans.value</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)])))</span></code></pre><p>This uses the handy <code>static</code> syntax class that comes with <code>syntax/parse</code>, which implicitly handles the call to <code>syntax-local-value</code> and produces a nice error message if the value returned does not match a predicate. All we have to do is apply the transformer value bound to the <code>trans.value</code> attribute using <code>local-apply-transformer</code>, and now the <code>expand-macro</code> can be written in just a couple lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">expand-inside</span> +<span class="w"> </span><span class="kd">#:track-literals</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:do-expand-inside</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form.expansion</span><span class="p">])</span></code></pre><p>(Using the <code>#:track-literals</code> option, also new in Racket v7.0, ensures that Check Syntax will be able to recognize the uses of <code>$expand</code> that disappear from after <code>expand-inside</code> is expanded.)</p><p>Putting everything together, our example from above really works:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>That’s it. All told, the entire implementation is only about 30 lines of code. For a full, compilable, working example, see <a href="https://gist.github.com/lexi-lambda/65d69043023b519694f50dfca2dc7d33">this gist</a>.</p><ol class="footnotes"></ol></article>Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions2018-09-13T00:00:00Z2018-09-13T00:00:00ZAlexis King<article><p>In my <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">previous blog post</a>, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.</p><h2><a name="what-are-internal-definitions"></a>What are internal definitions?</h2><p>This blog post is going to be largely focused on how to properly implement a form that handles the expansion of <em>internal definitions</em> in Racket. This is a tricky topic to get right, but before we can discuss internal definitions, we have to establish what definitions themselves are and how they relate to other binding forms.</p><p>In a traditional Lisp, there are two kinds of bindings: top-level bindings and local bindings. In Scheme and its descendants, this distinction is characterized by two different binding forms, <code>define</code> and <code>let</code>. To a first approximation, <code>define</code> is used for defining top-level, global bindings, and it resembles variable definitions in many mainstream languages in the sense that definitions using <code>define</code> are not really expressions. They don’t produce a value, they define a new binding. Definitions written with <code>define</code> look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="s2">"hello"</span><span class="p">)</span></code></pre><p>Each definition is made up of two parts: the <em>binding identifier</em>, in this case <code>x</code> and <code>y</code>, and the <em>right hand side</em>, or RHS for short. Each RHS is a single expression that will be evaluated and used as the value for the introduced binding.</p><p>In Scheme and Racket, <code>define</code> also supports a shorthand form for defining functions in a natural syntax without the explicit need to write <code>lambda</code>, which looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">double</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span></code></pre><p>However, this is just syntactic sugar. The above form is really just a macro for the following equivalent, expanded version:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">double</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)))</span></code></pre><p>Since we only care about fully-expanded programs, we’ll focus exclusively on the expanded version of <code>define</code> in this blog post, since if we handle that, we’ll also handle the function shorthand’s expansion.</p><p>In contrast to <code>define</code>, there is also <code>let</code>, which has a rather different shape. A <code>let</code> form <em>is</em> an expression, and it creates local bindings in a delimited scope:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>The binding clauses of a <code>let</code> expression are known as the <em>binding pairs</em>, and the sequence of expressions afterwards are known as the <em>body</em> of the <code>let</code>. Each binding pair consists of a binding identifier and a RHS, just like a top-level definition created with <code>define</code>, but while <code>define</code> is a standalone form, the binding pairs cannot meaningfully exist outside of a <code>let</code>—they are recognized as part of the grammar of the <code>let</code> form itself.</p><p>Like other Lisps, Racket distinguishes between top-level—or, more precisely, <em>module-level</em>—bindings and local bindings. A module-level binding can be exported using <code>provide</code>, which will allow other modules to access the binding by importing the module with <code>require</code>. Such definitions are treated specially by the macroexpander, compiler, and runtime system alike. There is a pervasive, meaningful difference between module-level definitions and local definitions besides simply scope.</p><p>I am making an effort to make this as clear as possible before discussing internal definitions because without it, the following point can be rather confusing: internal definitions are written using <code>define</code>, but they are local bindings, <em>not</em> module-level ones! In Racket, <code>define</code> is allowed to appear in the body of virtually all block forms like <code>let</code>, so the following is a legal program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>This program is equivalent to the one expressed using <code>let</code>. In fact, when the Racket macroexpander expands these local uses of <code>define</code>, it actually translates them into uses of <code>letrec</code>. After expanding the above expression, it would look closer to the following:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span></code></pre><p>In this sense, <code>define</code> is a form with a double life in Racket. When used at the module level, it creates module-level definitions, which remain in a fully-expanded program and can be imported by other modules. When used inside local blocks, it creates internal definitions, which do not remain in fully expanded programs, since they are translated into recursive local binding forms.</p><p>In this blog post, we will ignore module-level definitions. Like in the previous blog post, we will focus exclusively on expanding expressions, not whole modules. However, we will extend our language to allow internal definitions inside local binding forms, and we will translate them into <code>letrec</code> forms in the same way as the Racket macroexpander.</p><h2><a name="revisiting-and-generalizing-the-expression-expander"></a>Revisiting and generalizing the expression expander</h2><p>In the previous blog post, our expander expanded types, which were essentially expressions from the perspective of the Racket macroexpander. We wrote a syntax class that handled the parsing of a restricted type grammar that disallowed most Racket-level expression forms, like <code>begin</code>, <code>if</code>, <code>#%plain-lambda</code>, and <code>quote</code>. After all, Hackett is not dependently-typed, and it disallows explicit type abstraction to preserve type inference, so it would be a very bad thing if we allowed <code>if</code> or explicit lambda abstraction to appear in our types. For this blog post, however, we will restructure the type expander to handle the full grammar of expressions permitted by Racket.</p><p>While the syntax class approach used in the previous blog post was cute, this blog post will use ordinary functions defined at phase 1 instead of syntax classes. In practice, this provides superior error reporting, since it reports syntax errors in terms of the form that went wrong, not the form prior to expansion. Since we can still use <code>syntax-parse</code> to parse the arguments to these functions, we don’t lose any expressive power in the expression of our pattern language.</p><p>To start, we’ll extract the call to <code>local-expand</code> into its own function. This corresponds to the <code>type</code> syntax class from the previous blog post, but we’ll use phase 1 parameters to avoid threading so many explicit function arguments around:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-context</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-stop-list</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">))))</span></code></pre><p>Due to the way <code>local-expand</code> implicitly extends the stop list, as discussed in the previous blog post, we can initialize the stop list to a list containing just <code>define-values</code> and <code>define-syntaxes</code>, and the other forms we care about will be included automatically.</p><p>Next, we’ll use this function to implement a <code>expand-expression</code> function, which will emulate the way the expander expands a single expression, as the name implies. We’ll ignore any custom core forms for now, so we’ll just focus exclusively on the Racket core forms.</p><p>A few of Racket’s core forms are not actually subject to any expansion at all, and they expand to themselves. These forms are <code>quote</code>, <code>quote-syntax</code>, and <code>#%variable-reference</code>. Additionally, <code>#%top</code> is not something useful to handle ourselves, since it involves no recursive expansion, so we’ll treat it as if it expands to itself as well and allow the expander to raise any unbound identifier errors it produces. Here’s what the <code>expand-expression</code> function looks like when exclusively handling these things:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Another set of Racket core forms are simple expressions which contain subforms, all of which are themselves expressions. These forms include things like <code>#%expression</code>, <code>begin</code>, and <code>if</code>, and they can be expanded recursively. We’ll add another clause to handle these, which can be written with a straightforward recursive call to <code>expand-expression</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Another easy form to handle is <code>set!</code>, since it also requires simple recursive expansion, but it can’t be handled in the same way as the above forms since one of its subforms (the variable to mutate) should not be expanded. It needs another small clause:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:set!</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)))]</span></code></pre><p>The other expressions are harder, since they’re all the binding forms. Fully-expanded Racket code has four local binding forms: <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, and <code>letrec-values</code>. Additionally, as discussed in the previous blog post, <code>local-expand</code> can also produce <code>letrec-syntaxes+values</code> forms produced by local syntax bindings. In the type expander, we completely disallowed runtime bindings from appearing in the resulting program, so we completely removed <code>letrec-syntaxes+values</code> in our expansion, but in the case of handling arbitrary Racket programs, we actually want to leave a <code>letrec-values</code> form behind to hold any runtime bindings (i.e. the <code>values</code> part of <code>letrec-syntaxes+values</code>).</p><p>We’ll start with <code>#%plain-lambda</code>, which is the simplest of all the five aforementioned binding forms. It binds a sequence of identifiers at runtime, and they are in scope within the body of the lambda expression. Just as we created and used an internal-definition context to hold the bindings of a <code>letrec-syntax+values</code> form in the previous blog post, we’ll do the same for Racket’s other binding forms as well:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>However, the above handling of <code>#%plain-lambda</code> isn’t <em>quite</em> right, since the argument list can also include a “rest argument” binding in addition to a sequence of positional arguments. To accommodate this, we can introduce a simple syntax class that handles the different permutations:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">plain-formals</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"formals"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[[</span><span class="n">id</span><span class="w"> </span><span class="mi">1</span><span class="p">]]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id*:id</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id**:id</span><span class="p">)</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">id*</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">id**</span><span class="p">]]))</span></code></pre><p>Now we can use this to adjust <code>#%plain-lambda</code> to handle rest arguments:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Next, we’ll handle <code>case-lambda</code>. As it turns out, expanding <code>case-lambda</code> is almost exactly the same as expanding <code>#%plain-lambda</code>, except that it has multiple clauses. Since each clause is expanded identically to the body of a <code>#%plain-lambda</code>, and it even has the same shape, the clauses can be extracted into a separate syntax class to share code between the two forms:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]]))</span></code></pre><p>Now, both <code>#%plain-lambda</code> and <code>case-lambda</code> can be handled in a few lines of code each:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Finally, we need to tackle the three <code>let</code> forms. None of these involve any fundamentally new ideas, but they are a little bit more involved than the variants of lambda due to the need to handle the RHSs. Each variant is slightly different, but not dramatically so: the bindings aren’t in scope when expanding the RHSs of <code>let-values</code>, but they are for <code>letrec-values</code> and <code>letrec-syntaxes+values</code>, and <code>letrec-syntaxes+values</code> creates transformer bindings and must evaluate some RHSs in phase 1 while <code>let-values</code> and <code>letrec-values</code> exclusively bind runtime bindings. It would be possible to implement these three forms in separate clauses, but since we’d ideally like to duplicate as little code as possible, we can write a rather elaborate <code>syntax/parse</code> pattern to handle all three binding forms all at once.</p><p>We’ll start by handling <code>let-values</code> alone to keep things simple:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>This isn’t dramatically different from the implementation of <code>#%plain-lambda</code>. The only difference is that we have to recursively invoke <code>expand-expression</code> on the RHSs in addition to expanding the body expressions. To handle <code>letrec-values</code> in the same clause, however, we’ll have to get a little more creative.</p><p>So far, we haven’t actually tapped very far into <code>syntax/parse</code>’s pattern language over the course of these two blog posts. The full language available to patterns is rather extensive, and we can take advantage of that to write a modification of the above clause that handles both <code>let-values</code> and <code>letrec-values</code> at once:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}}}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>The <code>~bind</code> pattern allows us to explicitly control how attributes are bound as part of the pattern-matching process, which allows us to track when we want to enable the recursive binding behavior of <code>letrec-values</code> in our handler code. Since the vast majority of the logic is otherwise identical, this is a significant improvement over duplicating the clause.</p><p>Adding support for <code>letrec-syntaxes+values</code> is done in the same general way, but the pattern is even more involved. In addition to tracking whether or not the bindings are recursive, we have to track if any syntax bindings were present at all, and if they were, bind them with <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span></code></pre><p>This behemoth clause handles all three varieties of <code>let</code> forms that can appear in the result of <code>local-expand</code>. Notably, in the <code>letrec-syntaxes+values</code> case, we expand into <code>letrec-values</code>, since the transformer bindings are effectively erased, and we use <code>syntax-track-origin</code> to record that the result originally came from a use of <code>letrec-syntaxes+values</code>.</p><p>With these five clauses, we’ve handled all the special forms that can appear in expression position in Racket’s kernel language. To tie things off, we just need to handle the cases of a variable reference, which is represented by a bare identifier not bound to syntax, or literal data, like numbers or strings. We can add one more clause at the end to handle those:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span></code></pre><p>Putting them all together, our <code>expand-expression</code> function looks as follows:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> + +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">])))</span></code></pre><p>If we try it out, we’ll see that it really does work! Even complicated local binding forms are handled properly by our expander:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">))))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">(((</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>We are now able to expand arbitrary Racket expressions in the same way that the expander does. While this might not seem immediately useful—after all, we haven’t actually gained anything here over just calling <code>local-expand</code> with an empty stop list—we can use this as the basis of an expander that can extensibly handle custom core forms, which I may cover in a future blog post.</p><h2><a name="adding-support-for-internal-definitions"></a>Adding support for internal definitions</h2><p>In the previous section, we defined an expander that could expand arbitrary Racket expressions, but our expander is still imperfect: we still do not support internal definitions. For all forms that have bodies, including <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, and <code>letrec-syntaxes+values</code>, Racket permits the use of internal definitions.</p><p>In practice, internal-definition contexts allow for an increased degree of modularity compared to traditional local binding forms, since they provide an <em>extensible</em> binding language. Users may mix many different binding forms within a single definition context, such as <code>define</code>, <code>define-syntax</code>, <code>match-define</code>, and even <code>struct</code>. However, this means the rewriting process described earlier in this blog post is not as simple as detecting the definitions and lifting them into a local binding form, since it’s not immediately apparent which forms are binding forms and which are expressions!</p><p>For this reason, expanding internal-definition contexts happens to be a nontrivial problem in itself. It involves a little more care than expanding expressions does, since it requires using partial expansion to discover which forms are definitions and which forms are expressions. We must take care to never expand too much, but also to expand enough that we reveal all uses of <code>define-values</code> and <code>define-syntaxes</code> (which all definition forms eventually expand into). We also must handle the splicing behavior of <code>begin</code>, which is necessary to allow single forms to expand into multiple definitions.</p><p>We’ll start by writing an <code>expand-body</code> function, which operates similarly to our previous <code>expand-expression</code> function. Unlike <code>expand-expression</code>, <code>expand-body</code> will accept a <em>list</em> of syntax objects, which represents the sequence of forms that make up the body. Logically, each body will create a first-class definition context with <code>syntax-local-make-definition-context</code> to represent the sequence of definitions:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>The bulk of our <code>expand-body</code> function will be a loop that partially expands body forms, adds definitions to the definition context as it discovers them, and returns the expressions and runtime definitions to be rewritten into binding pairs for a <code>letrec-values</code> form. Additionally, the loop will also track so-called <em>disappeared uses</em> and <em>disappeared bindings</em>, which are attached to the expansion using syntax properties to allow tools like DrRacket to learn about the binding structure of phase 1 definitions that are erased as part of macroexpansion.</p><p>The skeleton of this loop is relatively straightforward to write. We will iterate over the syntax objects that make up the body, expand them, and process the expansion using <code>syntax-parse</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">)))))))</span></code></pre><p>The hard part, of course, is actually handling the potential results of that expansion. We need to handle three forms specially: <code>begin</code>, <code>define-values</code>, and <code>define-syntaxes</code>. All other results of partial expansion will be treated as expressions. We’ll start by handling <code>begin</code>, since it’s the simplest case; we only need to prepend the subforms to the list of body forms to be processed, then continue looping:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>However, as is often the case, this isn’t quite perfect, since the information that these forms came from a surrounding <code>begin</code> is lost, which tools like DrRacket want to know. To solve this problem, the expander adjusts the <code>origin</code> property for all spliced forms, which we can mimic using <code>syntax-track-origin</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This is sufficient for <code>begin</code>, so we can move onto the actual definitions themselves. This actually isn’t too hard, since we just need to add the bindings we discover to the first-class definition context and preserve <code>define-values</code> bindings as binding pairs:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This solution is missing one thing, however, which is the use of <code>syntax-local-identifier-as-binding</code> to any use-site scopes that were added to the binding identifier while expanding the binding form in the definition context. Explaining precisely why this is necessary is outside the scope of this blog post, and is best understood by reading <a href="http://www.cs.utah.edu/plt/scope-sets/pattern-macros.html#%28part._use-site%29">the section on use-site scopes</a> in the paper that describes the theory behind Racket’s current macro system, Bindings as Sets of Scopes. In any case, the impact on our implementation is small:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>Finally, as with <code>begin</code>, we want to track that the binding pairs we generate actually came from a use of <code>define-values</code> (which in turn likely came from a use of some other definition form). Therefore, we’ll add another use of <code>syntax-track-origin</code> to copy and extend the necessary properties:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>That’s it for <code>define-values</code>. All that’s left is to handle <code>define-syntaxes</code>, which is quite similar, but instead of storing the definition in a binding pair, its RHS is immediately evaluated and added to the definition context using <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span></code></pre><p>As the above snippet indicates, this is also where the disappeared uses and disappeared bindings come in. In previous cases, we’ve used <code>syntax-track-origin</code> to indicate that a piece of syntax was the result of expanding a different piece of syntax, but in this case, <code>define-syntaxes</code> doesn’t expand into anything at all; it’s simply removed from the expansion entirely. Therefore, we need to resort to tracking the information in syntax properties on the resulting <code>letrec-values</code> form, so we’ll save them for later.</p><p>Finally, to finish things up, we can add a catchall clause that handles all other forms, which are now guaranteed to be expressions:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This completes our loop that processes definition forms, so all that’s left to do is handle the results. The only significant remaining work is to actually expand the RHSs of the binding pairs we collected and the body expressions, which can be done by calling our own <code>expand-expression</code> function directly:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span></code></pre><p>Finally, we can assemble all the pieces together into a single local binding form with the appropriate syntax properties:</p><pre><code class="pygments"><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))</span></code></pre><p>That’s it. We’ve now written an <code>expand-body</code> function that can process internal definition contexts in the same way that the macroexpander does. Overall, the whole function is just under 45 lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)))))</span></code></pre><p>The next step is to actually use this function. We need to replace certain recursive calls to <code>expand-expression</code> with calls to <code>expand-body</code>, but if we do this naïvely, we’ll have some problems. Currently, when we expand body forms, they’re always immediately inside another definition context (i.e. the bindings introduced by lambda formals or by <code>let</code> binding pairs), but they haven’t actually been expanded in that context yet. When we call <code>expand-body</code>, we create a nested context, which will inherit the bindings, but won’t automatically add the parent context’s scope. Therefore, we need to manually call <code>internal-definition-context-introduce</code> on the body syntax objects before calling <code>expand-body</code>. We can write a small helper function to make this easier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="n">stxs</span><span class="w"> </span><span class="n">ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">ctx</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stxs</span><span class="p">))))))</span></code></pre><p>Now we just need to replace the relevant calls to <code>expand-expression</code> with calls to <code>expand-body/in-ctx</code>, starting with a minor adjustment to our <code>lambda-clause</code> syntax class from earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="p">]]))</span></code></pre><p>The only other change must occur in the handling of the various <code>let</code> forms, which similarly replaces <code>expand-expression</code> with <code>expand-body/in-ctx</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">)))]</span></code></pre><p>With these changes, we’ve now extended our expression expander with the ability to expand internal definitions. We can see this in action on a simple example:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>Just as we’d like, the transformer bindings were expanded and subsequently eliminated, and the runtime binding was collected into a <code>letrec-values</code> form. The outer <code>let-values</code> is left over from the outer <code>let</code>, which is needed only to create an internal-definition context to hold our internal definitions.</p><h2><a name="putting-the-expression-expander-to-work"></a>Putting the expression expander to work</h2><p>So far, we’ve done a lot of work to emulate the behavior of Racket’s macroexpander, and as the above example demonstrates, we’ve been fairly successful in that goal. However, you might be wondering <em>why</em> we did any of this, as replicating the behavior of <code>local-expand</code> is not very useful on its own. As mentioned above, this can be used as the foundation of an expander for custom core forms that extends, rather than replaces, the built-in Racket core forms, It can also be used to “cheat” and expand through the behavior of the <code>local-expand</code> stop list, which implicitly adds the Racket core forms to any non-empty stop list. Hopefully, I’ll have a chance to cover some of these things more deeply in the future, but for now, I’ll just give a small taste of the latter.</p><p>By using the power of our <code>expand-expression</code> function, it’s actually possible to use this kind of expression expander to do genuinely nefarious things, such as hijack the behavior of arbitrary macros! For example, we could do something evil like make <code>for</code> loops run in reverse order by adding <code>for</code> to <code>current-stop-list</code>, then adding an additional special case to <code>expand-expression</code> for <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">for</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="k">for</span><span class="p">]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:for</span><span class="w"> </span><span class="p">([</span><span class="n">x:id</span><span class="w"> </span><span class="n">seq:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="p">(</span><span class="nb">sequence-&gt;list</span><span class="w"> </span><span class="n">seq</span><span class="p">)))]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>Amazingly, due to the fact that we’ve taken complete control of the expansion process, this will rewrite uses of <code>for</code> <em>even if they are introduced by macroexpansion</em>. For example, we could write a small macro that expands into a use of <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="nb">in-range</span><span class="w"> </span><span class="n">n</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">i</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span> +<span class="mi">0</span> +<span class="mi">1</span> +<span class="mi">2</span> +<span class="mi">3</span> +<span class="mi">4</span></code></pre><p>If we write a wrapper macro that applies our evil version of <code>expand-expression</code> to its body, then wrap a use of our <code>print-up-to</code> macro with it, it will execute the loop in reverse order:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:expr</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span><span class="p">)])</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>On its own, this is not that impressive, since we could have just used <code>local-expand</code> on the body directly to achieve this. However, what’s remarkable about <code>hijack-for-loops</code> is that it will work even if the <code>for</code> loop is buried deep inside some arbitrary expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">foo</span> +<span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">))))</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="mi">5</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>Of course, this example is rather contrived—mucking with <code>for</code> loops like this isn’t useful at all, and nobody would really write <code>print-up-to</code> as a macro, anyway—but there is potential for using this technique to do more interesting things.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p>The system outlined in this blog post is not something I would recommend using in any real macro. It is enormously complicated, requires knowledge well above that of your average working macrologist, and it involves doing rather horrible things to the macro system, things it was undoubtably never designed to do. Still, I believe this blog post is useful, for a few different reasons:</p><ol><li><p>The technology outlined in this post, while perhaps not directly applicable to existing real-world problems, provides a framework for implementing various new kinds of syntax transformations in Racket <em>without</em> extending the macro system. It demonstrates the expressive power of the macro system, and it hopefully lays the foundation for a better, more high-level interface for users who wish to define their own languages with custom core forms.</p></li><li><p>This system provides insight into the way the Racket macroexpander operates, <em>in terms of the userspace syntax API</em>. The canonical existing model of hygienic macroexpansion, in the aforementioned <a href="http://www.cs.utah.edu/plt/scope-sets/">Bindings as Sets of Scopes</a> paper, does not explain the workings of internal definition contexts in detail, and it certainly doesn’t explain them in terms that a Racket programmer would already be familiar with. By reencoding those ideas within the macro system itself, an advanced macro writer may be able to more easily connect concepts in the macro system’s implementation to concepts they have already been exposed to.</p></li><li><p>The capability of the proof-of-concept outlined here demonstrates that the limitation imposed by the existing implementation of the stop list (namely, the way it is implicitly extended with additional identifiers) is essentially artificial, and it can be hacked around with sufficient (albeit significant) effort. This isn’t enormously important, but it is somewhat relevant to a recent debate in <a href="https://github.com/racket/racket/issues/2154">a GitHub issue</a> about the handling of the <code>local-expand</code> stop list.</p></li><li><p>Finally, for myself as much as anyone else, this implementation records in a concise way (perhaps overly concise at times) the collection of very subtle details I’ve learned over the past six months about how information is preserved and propagated during the expansion process.</p></li></ol><p>This blog post is not for everybody. If you made it to the end, give yourself a pat on the back. If you made it to the end <em>and</em> understood everything you read: congratulations, you are a certified expert in Racket macro programming. If not, do not fear, and do not lose hope—I plan for something significantly more mellow next time.</p><p>As always, I’d like to give thanks to the people who contributed significantly, if indirectly, to the contents of this blog post, namely <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://mballantyne.net">Michael Ballantyne</a>, and <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a>. And finally, for those interested, all of the code in this blog post can be found in a runnable form <a href="https://gist.github.com/lexi-lambda/c4f4b91ac9c0a555447d72d02e18be7b">in this GitHub gist</a>.</p><ol class="footnotes"></ol></article>Reimplementing Hackett’s type language: expanding to custom core forms in Racket2018-04-15T00:00:00Z2018-04-15T00:00:00ZAlexis King<article><p>In the past couple of weeks, I <a href="https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935">completely rewrote the implementation of Hackett’s type language</a> to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.</p><p>This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">my previous blog post on Hackett</a>, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.</p><h2><a name="what-are-core-forms"></a>What are core forms?</h2><p>Before we can get started writing <em>custom core forms</em>, we need to understand the meaning of Racket’s plain old <em>core forms</em>. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.</p><p>To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a <code>#lang</code> line at the top. In this case, we’ll use <code>#lang racket</code> to keep things simple:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as <em>reading</em> the program. The <code>#lang</code> line controls how the program is read—some <code>#lang</code>s provide parsers that allow syntax that is very different from the parser used for <code>#lang racket</code>—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span></code></pre><p>Note the introduction of <code>#%module-begin</code>. Despite the fancy name, this is really just an ordinary macro provided by the <code>racket</code> language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with <code>#%module-begin</code>; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.</p><p>One the program has been read, it is subsequently <em>expanded</em> by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">2</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">call-with-values</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="n">add2</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">))</span> +<span class="w"> </span><span class="n">print-values</span><span class="p">)))</span></code></pre><p>Let’s note the things that changed:</p><ol><li><p><code>#%module-begin</code> was replaced with <code>#%plain-module-begin</code>. <code>#%plain-module-begin</code> is a binding that wraps the body of every expanded module, and all definitions of <code>#%module-begin</code> in any language must eventually expand to <code>#%plain-module-begin</code>. However, <code>#lang racket</code>’s <code>#%module-begin</code> doesn’t <em>just</em> expand to <code>#%plain-module-begin</code>, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints <code>5</code> even though there is no code related to printing in the original program!</p></li><li><p>The lambda shorthand used with <code>define</code> was converted to an explicit use of <code>lambda</code>, and it was expanded to <code>define-values</code>. In Racket, <code>define</code> and <code>define-syntax</code> are really just macros for <code>define-values</code> and <code>define-syntaxes</code> that only bind a single identifier.</p></li><li><p>All function applications were tagged explicitly with <code>#%plain-app</code>. This syntactically distinguishes function applications from uses of forms like <code>define-values</code> or <code>lambda</code>. It also allows languages to customize function application by providing their own macros named <code>#%app</code> (just like languages can provide their own macros named <code>#%module-begin</code> that expand to <code>#%plain-module-begin</code>), but that is outside the scope of this blog post.</p></li><li><p>All literals have been wrapped with <code>quote</code>, so <code>2</code> became <code>'2</code> and <code>3</code> became <code>'3</code>.</p></li></ol><p>Importantly, the resulting program contains <strong>no macros</strong>. Such programs are called <em>fully expanded</em>, since all macros have been eliminated and no further expansion can take place.</p><p>So what’s left behind? Well, some of the things in the program are literal data, like the numbers <code>2</code> and <code>3</code>. There are also some variable references, <code>x</code> and <code>add2</code>. Most of the program, however, is built out of primitives like <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, and <code>lambda</code>. These primitives are <em>core forms</em>—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.</p><p>In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:</p><pre><code class="pygments"><span class="n">var</span> <span class="n">add2</span> <span class="o">=</span> + <span class="n">function</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span> + +<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">add2</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span></code></pre><p>Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be <em>compiled</em>. The Racket compiler has built-in rules for how to compile core forms like <code>define-values</code>, <code>lambda</code>, and <code>#%plain-app</code>, and the result is optimized Racket bytecode.</p><p>In the remainder of this blog post, as most discussions of macros do, we’ll ignore the <em>read</em> and <em>compile</em> steps of the Racket program pipeline and focus exclusively on the <em>expand</em> step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.</p><h3><a name="racket-s-default-core-forms"></a>Racket’s default core forms</h3><p>So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, <code>lambda</code>, and <code>quote</code>, but there are many more. The full list is available in the section of the Racket reference named <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29">Fully Expanded Programs</a>, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like <code>define-syntaxes</code>, <code>if</code>, <code>let-values</code>, <code>letrec-values</code>, <code>begin</code>, <code>quote-syntax</code>, and <code>set!</code>. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.</p><p>These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, <code>#lang racket</code> implements <code>cond</code> as a macro that eventually expands into <code>if</code>, and it implements <code>syntax</code> as a macro that eventually expands into function calls and <code>quote-syntax</code>. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s <code>match</code> can expand into uses of <code>let</code> and <code>cond</code>, and it doesn’t need to concern itself with using <code>let-values</code> and <code>if</code>. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.</p><h3><a name="the-need-for-custom-core-forms"></a>The need for custom core forms</h3><p>With that in mind, why might we wish to define <em>custom</em> core forms? In fact, what would such a thing even mean? By their very nature, <em>all</em> Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.</p><p>Despite this, there <em>are</em> at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.</p><h4><a name="supporting-multiple-backends"></a>Supporting multiple backends</h4><p>The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket <code>#lang</code> that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might <em>also</em> wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.</p><p>In the case of a hardware description language, the custom core forms might include things like <code>input</code> and <code>output</code> for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could <em>additionally</em> define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.</p><p>Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.</p><h4><a name="compiling-an-extensible-embedded-language"></a>Compiling an extensible embedded language</h4><p>A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.</p><p>This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it <em>will</em> eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.</p><p>Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the <code>match</code> macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.</p><p>Existing DSLs of this type are rare, but they do exist. <code>syntax/parse</code> provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from <code>racket/match</code> to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While <code>match</code> is essentially just a traditional pattern-matcher, albeit an extensible one, <code>syntax-parse</code> is its own programming language, closer in some ways to Prolog than to Racket.</p><p>For this reason, <code>syntax/parse</code> has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as <a href="http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29"><em>pattern directives</em></a>. Here is an example of pattern directives in action, from my own <code>threading</code> library:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>Each directive is represented by a keyword, in this case <code>#:do</code> and <code>#:with</code>. Each directive has a corresponding keyword in the pattern language, in this case <code>~do</code> and <code>~parse</code>. Therefore, the above pattern could equivalently be written this way:</p><pre><code class="pygments"><span class="p">[{</span><span class="n">~and</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">~do</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using <code>#:declare</code>, so this is also equivalent:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">expr</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a <code>#:with</code> or <code>~parse</code> pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using <code>#:do</code> or <code>~do</code>, practical uses of <code>syntax/parse</code> really do involve quite a lot of programming in its pattern DSL.</p><p>But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—<code>#:declare</code>, <code>#:do</code>, and <code>#:with</code>, among others—are essentially the core forms of <code>syntax/parse</code>’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.</p><p>But what if <code>syntax/parse</code> could define its own core forms? What if, instead of <code>#:do</code>, <code>#:declare</code>, and <code>#:with</code> being implemented as keyword options specially recognized by the <code>syntax-parse</code> grammar, it defined <code>do</code>, <code>declare</code>, and <code>with</code> as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the <code>syntax/parse</code> core forms. The implementation of <code>syntax/parse</code> could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.</p><p>Now, to be fair, <code>syntax/parse</code> is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.</p><p>The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.</p><h2><a name="the-need-for-a-custom-type-language-in-hackett"></a>The need for a custom type language in Hackett</h2><p>In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for <em>both</em> of them:</p><ul><li><p>Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.</p></li><li><p>Hackett can <em>also</em> make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.</p></li></ul><p>The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:</p><ul><li><p><code>(#%type:con <em>id</em>)</code> — Type constructors, like <code>Integer</code> or <code>Maybe</code>. These are one of the fundamental building blocks of Hackett types.</p></li><li><p><code>(#%type:app <em>type</em> <em>type</em>)</code> — Type application, such as <code>(Maybe Integer)</code>. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of <code>#%type:app</code>.</p></li><li><p><code>(#%type:forall <em>id</em> <em>type</em>)</code> — Universal quantification. This is essentially a binding form, which binds any uses of <code>(#%type:bound-var <em>id</em>)</code> in <code><em>type</em></code>.</p></li><li><p><code>(#%type:qual <em>type</em> <em>type</em>)</code> — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like <code>Eq</code> are bound as type constructors.</p></li><li><p>Finally, Hackett types support three different varieties of type variables:</p><ul><li><p><code>(#%type:bound-var <em>id</em>)</code> — Bound type variables. These are only legal under a corresponding <code>#%type:forall</code>.</p></li><li><p><code>(#%type:wobbly-var <em>id</em>)</code> — Solver variables, which may unify with any other type as part of the typechecking process.</p></li><li><p><code>(#%type:rigid-var <em>id</em>)</code> — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.</p></li></ul></li></ul><p>To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we <em>can</em> bind them to macros that do nothing but raise an exception if something attempts to expand them:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span></code></pre><p>This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.</p><h3><a name="expanding-types-in-our-type-language"></a>Expanding types in our type language</h3><p>We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we <em>do</em> want to expand some of their subforms. For example, in the type <code>(#%type:app a b)</code>, we want to recursively expand <code>a</code> and <code>b</code>.</p><p>In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a>, and it has an option relevant to our needs: the stop list.</p><p>Often, <code>local-expand</code> is used to force the expander to completely, recursively expand a form. For example, by using <code>local-expand</code>, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="c1">; =&gt; (let-values ([(x) &#39;1]) (#%plain-app + x &#39;2))</span></code></pre><p>The third argument to <code>local-expand</code> is the <em>stop list</em>, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; =&gt; (#%type:forall x t)</span></code></pre><p>Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the <code>#%type:forall</code> identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding <code>t</code> since the expander has no way of knowing which pieces of <code>(#%type:forall x t)</code> it should expand! In this case, we want it to recur to expand <code>t</code>, since it should be a type, but not <code>x</code>, since <code>#%type:forall</code> essentially puts <code>x</code> in binding position.</p><p>Therefore, we have to get more clever. We need to call <code>local-expand</code> to produce a type, then we have to pattern-match on it and subsequently call <code>local-expand</code> <em>again</em> on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.</p><p>One good way to do this is to use <code>syntax/parse</code> syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using <code>local-expand</code> and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled <code>#%type:con</code> and <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:expanded-type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]))</span></code></pre><p>This blog post is definitely <em>not</em> a <code>syntax/parse</code> tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named <code>expansion</code>. This attribute contains the fully expanded version of the type currently being parsed. In the <code>#%type:con</code> case, <code>expansion</code> is just <code>this-syntax</code>, which holds the current piece of syntax being parsed. This makes sense, since uses of <code>#%type:con</code> just expand to themselves—expanding <code>(#%type:con Maybe)</code> should not perform any additional expansion on <code>Maybe</code>. This is one of Hackett’s atomic types.</p><p>In contrast, <code>#%type:app</code> <em>does</em> recursively expand its arguments. By annotating its two subforms with <code>:type</code>, the <code>type</code> syntax class will invoke <code>local-expand</code> on each subform, which will in turn use <code>expanded-type</code> to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once <code>a</code> and <code>b</code> have been expanded, <code>#%type:app</code> reassembles them into a new syntax object using <code>#'(#%type:app a.expansion b.expansion)</code>, which replaces their unexpanded versions with their new, expanded versions.</p><p>We can see this behavior by writing a small <code>expand-type</code> function that will expand its argument:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>Now we can use it to observe what happens when we try expanding a type using <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; #%type:app: expected type</span> +<span class="c1">; at: Maybe</span> +<span class="c1">; in: (#%type:app Maybe Integer)</span></code></pre><p>Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined <code>Maybe</code> or <code>Integer</code> anywhere. Let’s do so! We can define them as simple macros that expand into uses of <code>#%type:con</code>, which can be done easily using <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29"><code>make-variable-like-transformer</code></a> from <code>syntax/transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Maybe</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Integer</span><span class="p">)))</span></code></pre><p>Now, if we try expanding that same type again:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Integer))</span></code></pre><p>…it works! Neat. Now we just need to add the cases for the remaining forms in our type language:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">t:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like <code>let-syntax</code> to work in types. For example, we ought to be able to create a local type binding with <code>let-syntax</code> and have it just work. Unfortunately, it doesn’t:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; let-syntax: expected one of these identifiers: `#%type:con&#39;, `#%type:app&#39;, `#%type:forall&#39;, `#%type:qual&#39;, `#%type:bound-var&#39;, `#%type:wobbly-var&#39;, or `#%type:rigid-var&#39;</span> +<span class="c1">; at: letrec-syntaxes+values</span> +<span class="c1">; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))</span></code></pre><p>What went wrong? And why is it complaining about <code>letrec-syntaxes+values</code>? Well, if you read the documentation for <code>local-expand</code>, you’ll find that its behavior is a little more complicated than you might at first believe:</p><blockquote><p>If <em><code>stop-ids</code></em> is [a nonempty list containing more than just <code>module*</code>], then <code>begin</code>, <code>quote</code>, <code>set!</code>, <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, <code>if</code>, <code>begin0</code>, <code>with-continuation-mark</code>, <code>letrec-syntaxes+values</code>, <code>#%plain-app</code>, <code>#%expression</code>, <code>#%top</code>, and <code>#%variable-reference</code> are implicitly added to <em><code>stop-ids</code></em>. Expansion stops when the expander encounters any of the forms in <em><code>stop-ids</code></em>, and the result is the partially-expanded form.</p></blockquote><p>That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with <code>letrec-syntaxes+values</code> (which <code>let-syntax</code> expands to) is a reasonable one. If the expander naïvely expanded <code>letrec-syntaxes+values</code> in the presence of a nonempty stop list, it could cause some significant problems!</p><p>Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">))</span></code></pre><p>We see <code>let-syntax</code>, so we start by evaluating the expression on the right hand side of the <code>Bool</code> binding. This produces a transformer expression, so we bind <code>Bool</code> to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:</p><pre><code class="pygments"><span class="c1">; local bindings:</span> +<span class="c1">; Bool -&gt; #&lt;variable-like-transformer&gt;</span> +<span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)</span></code></pre><p>Now, the identifier in application position is <code>#%type:app</code>, and <code>#%type:app</code> is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the <code>let-syntax</code> needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is <code>(#%type:app Maybe Bool)</code>. But this is a problem, because when we then go to expand <code>Bool</code>, <code>Bool</code> isn’t in the local binding table anymore! The <code>let-syntax</code> was already erased, and <code>Bool</code> is unbound!</p><p>When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.</p><p>Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call <code>local-expand</code>. The bad news is that first-class definition contexts are <em>complicated</em>, and using them properly is a surprisingly subtle problem.</p><p>Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our <code>type</code> and <code>expanded-type</code> syntax classes so that we may thread a definition context around as we recursively expand:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now, we can add an additional case to <code>expanded-type</code> to handle <code>letrec-syntaxes+values</code>, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>But even this isn’t quite right. The problem with this implementation is that it throws away the existing <code>intdef-ctx</code> argument to <code>expanded-type</code>, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a <em>child</em> of the previous definition context by passing the old context as an argument to <code>syntax-local-make-definition-context</code>. This will ensure the parent bindings are brought into scope when expanding using the child context:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>With this in place, our example using <code>let-syntax</code> actually works!</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Bool))</span></code></pre><p>Pretty cool, isn’t it?</p><h3><a name="preserving-syntax-properties-and-source-locations"></a>Preserving syntax properties and source locations</h3><p>We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.</p><p>To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports <code>#%type:app</code> under a different name, like <code>#%type:apply</code>, we should expand to a piece of syntax that still has <code>#%type:apply</code> in application position instead of replacing it with <code>#%type:app</code>.</p><p>To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the <code>#%type:app</code> clause to the following:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span></code></pre><p>But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy <em>everything</em>, we can define two helper macros, <code>syntax/loc/props</code> and <code>quasisyntax/loc/props</code>, which are like <code>syntax/loc</code> and <code>quasisyntax/loc</code> but copy properties in addition to source location information:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span></code></pre><p>Using <code>syntax/loc/props</code>, we can be truly thorough about ensuring all properties are preserved:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span></code></pre><p>Applying this to the other relevant clauses, we get an updated version of the <code>expanded-type</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now we’re getting closer, but if you can believe it, even <em>this</em> isn’t good enough. The real expander’s implementation of <code>letrec-syntaxes+values</code> does two things our implementation does not: it copies properties and updates the <code>'origin</code> property to indicate the syntax came from a use of <code>letrec-syntaxes+values</code>, and it adds a <code>'disappeared-use</code> property to record the erased bindings for use by tools like DrRacket. We can apply <code>syntax-track-origin</code> and <code>internal-definition-context-track</code> to the resulting syntax to add the same properties the expander would:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span></code></pre><p>Now we’ve <em>finally</em> dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.</p><h3><a name="connecting-our-custom-language-to-hackett"></a>Connecting our custom language to Hackett</h3><p>It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-meta</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/parse</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/intdef</span> +<span class="w"> </span><span class="n">threading</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a <em>typechecker</em>. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.</p><p>But we don’t <em>just</em> need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom <code>#%app</code> binding that converts n-ary type applications to nested uses of <code>#%type:app</code>, as well as a nicer <code>forall</code> macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of <code>#%app</code>—one for types and one for values—since <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">Hackett supports multiple namespaces</a>, and each can have its own <code>#%app</code> binding.</p><p>The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom <code>syntax/parse</code> pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, <a href="https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt">it’s available on GitHub here</a>.</p><h2><a name="evaluation-limitations-and-acknowledgements"></a>Evaluation, limitations, and acknowledgements</h2><p>Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started <a href="https://twitter.com/lexi_lambda/status/976533916596097024">my new job</a> this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net <em>removal</em> of 100 lines from the rest of the codebase, which I consider an organizational win.</p><p>As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I <a href="https://twitter.com/lexi_lambda/status/985051504867446786">able to get it working</a> quickly and easily, I was able to do it in <a href="https://twitter.com/lexi_lambda/status/985052476473856000">no more than 20 lines of code</a>. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.</p><p>Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the <code>local-expand</code> stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes <code>syntax-parameterize</code>, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like <code>syntax-parameterize</code> cooperate better with things like Hackett’s type language.</p><p>Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> for <a href="https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf">his incredible work on constructing tools for the working macro developer</a>, including writing the fantastic <code>syntax/parse</code> library that powers essentially everything I do. Thank you both.</p><ol class="footnotes"></ol></article>User-programmable infix operators in Racket2017-08-12T00:00:00Z2017-08-12T00:00:00ZAlexis King<article><p>Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in <a href="https://github.com/lexi-lambda/hackett">Hackett</a>, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.</p><p>Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done <em>without</em> modifying the stock <code>#lang racket</code> reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.</p><h2><a name="our-mission"></a>Our mission</h2><p>Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we <em>do</em> want:</p><ul><li><p>Infix operators should be user-extensible, not limited to a special set of built-in operators.</p></li><li><p>Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.</p></li><li><p>We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like <code>cons</code>. This allows <code>5 - 1 - 2</code> to be parsed as <code>(- (- 5 1) 2)</code>, but <code>5 :: 1 :: nil</code> to be parsed as <code>(:: 5 (:: 1 nil))</code>.</p></li></ul><p>These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:</p><ul><li><p>We will <strong>not</strong> permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing <code>(+ 1 2)</code> with <code>{1 + 2}</code>.</p></li><li><p>Our implementation will <strong>not</strong> support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.</p></li><li><p>All operators will be binary, and we will <strong>not</strong> support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.</p></li></ul><p>With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"infix.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> + +<span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">{</span><span class="mi">10</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="c1">; =&gt; &#39;(1 7)</span></code></pre><p>Let’s get started.</p><h2><a name="implementing-infix-operators"></a>Implementing infix operators</h2><p>Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:</p><ol><li><p>How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?</p></li><li><p>How can we associate fixity information with certain operators?</p></li></ol><p>We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.</p><p>So, how <em>do</em> we detect if a Racket expression is surrounded by curly braces? Normally, in <code>#lang racket</code>, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated <em>exactly</em> the same as parentheses:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.</p><p>Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29"><em>syntax objects</em></a>. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as <a href="http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29"><em>syntax properties</em></a>. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s <a href="http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29"><em>reader</em></a> (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named <a href="http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29"><code>'paren-shape</code></a>.</p><p>This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="no">#f</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\[</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\{</span></code></pre><p>This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.</p><h3><a name="customizing-application"></a>Customizing application</h3><p>Racket is a language <em>designed</em> to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named <a href="http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29"><code>#%app</code></a>, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>…into this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>What’s special about <code>#%app</code> is that the macroexpander will use whichever <code>#%app</code> is in scope in the expression’s lexical context, so if we write our own version of <code>#%app</code>, it will be used instead of the one from <code>#lang racket</code>. This is what we will use to hook into ordinary Racket expressions.</p><p>To write our custom version of <code>#%app</code>, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the <code>'paren-shape</code> syntax property, <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29"><code>syntax/parse/class/paren-shape</code></a>. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>This code will transform any applications surrounded in curly braces into one that starts with <code>#%infix</code> instead of <code>#%app</code>, so <code>{1 + 2}</code> will become <code>(#%infix 1 + 2)</code>, for example. The identifier <code>#%infix</code> isn’t actually special in any way, it just has a funny name, but we haven’t actually defined <code>#%infix</code> yet, so we need to do that next!</p><p>To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like <code>{1 + 2}</code>, should be converted into the equivalent prefix expressions, in this case <code>(+ 1 2)</code>. We can do this with a simple macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)])</span></code></pre><p>Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the <code>#%app</code> from <code>racket/base</code>, which will avoid any accidental infinite recursion between our <code>#%app</code> and <code>#%infix</code>. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>That’s pretty cool!</p><p>Of course, we probably want to support infix applications with more than just a single binary operator, such as <code>{1 + 2 + 3}</code>. We can implement that just by adding another case to <code>#%infix</code> that handles more subforms:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">10</span></code></pre><p>I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">5</span></code></pre><p>Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as <code>cons</code>, nested <code>-&gt;</code> types or contracts for curried functions, and <code>expt</code>, the exponentiation operator.</p><h3><a name="tracking-operator-fixity"></a>Tracking operator fixity</h3><p>Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.</p><p>Essentially, Racket (like Scheme) uses a <code>define-syntax</code> form to define macros, which is what <code>define-syntax-parser</code> eventually expands into. However, unlike Scheme, Racket’s <code>define-syntax</code> is not <em>just</em> for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Once a binding has been defined using <code>define-syntax</code>, a macro can look up the value associated with it by using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, which returns the compile-time value associated with an identifier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">foo</span><span class="p">)))</span> +<span class="c1">; =&gt; 3</span></code></pre><p>The cool thing is that <code>syntax-local-value</code> gets the value associated with a specific <em>binding</em>, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use <code>syntax-local-value</code> to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the <code>foo</code> binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because <code>foo</code> is not bound to anything at runtime.</p><p>To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the <a href="http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29"><code>prop:procedure</code></a> structure type property.</p><p>If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">))))</span></code></pre><p>This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for <code>runtime-binding</code>, but we can also include a value of our choosing for <code>fixity</code>. Here’s an example:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="nb">cons</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="p">))</span></code></pre><p>This new <code>::</code> binding will act, in every way, just like <code>cons</code>. If we use it in the REPL, you can see that it acts exactly the same:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></code></pre><p>However, we can also use <code>syntax-local-value</code> to extract this binding’s fixity at compile-time, and that’s what makes it interesting:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">::</span><span class="p">))))</span> +<span class="c1">; =&gt; &#39;right</span></code></pre><p>Using this extra compile-time information, we can adjust our <code>#%infix</code> macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used <code>syntax/parse/class/paren-shape</code> to make decisions based on the <code>'paren-shape</code> syntax property, we can use <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29"><code>syntax/parse/class/local-value</code></a> to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span></code></pre><p>Now, we can update <code>#%infix</code> to use our new <code>infix-op</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span></code></pre><p>Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via <code>#:when</code> clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of <code>#%infix</code>, we can successfully use <code>::</code> in an infix expression, and it will be parsed with the associativity that we expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Exciting!</p><h3><a name="a-nicer-interface-for-defining-infix-operators"></a>A nicer interface for defining infix operators</h3><p>We currently have to define infix operators by explicitly using <code>define-syntax</code>, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the <code>define-infix-operator</code> form from the example at the very beginning of this blog post.</p><p>Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the <code>define-syntax</code> definitions we were already writing:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span></code></pre><p>With this in hand, we can define some infix operators with a much nicer syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary <code>#lang racket</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">-1</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">256</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">64</span></code></pre><p>And you know what’s most amazing about this? The entire thing is <strong>only 50 lines of code</strong>. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/local-value</span> +<span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span> +<span class="w"> </span><span class="n">syntax/transformer</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>Racket is a hell of a programming language.</p><h2><a name="applications-limitations-and-implications"></a>Applications, limitations, and implications</h2><p>This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am <em>almost certain</em> that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even <em>locally</em>.</p><p>What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as <code>let-syntax</code> and <code>syntax-parameterize</code>. Using these tools, it would be entirely possible to implement a <code>with-fixity</code> macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make <code>/</code> right associative within a block of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="m">1/6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="nb">/</span><span class="w"> </span><span class="n">right</span><span class="p">])</span> +<span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span> +<span class="mi">1</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of <code>splicing-let</code> and <code>splicing-let-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span> +<span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="n">op:id</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}}]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">op-tmp</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">op</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let</span><span class="w"> </span><span class="p">([</span><span class="n">op-tmp</span><span class="w"> </span><span class="n">op</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">op-tmp</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span></code></pre><p>This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations <em>between</em> operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.</p><p>As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in <a href="https://github.com/lexi-lambda/hackett">Hackett</a> to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow <em>any</em> expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.</p><p>If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt">this file for the definition of infix bindings</a>, as well as <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101">this file for the defintion of infix application</a>. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like <code>{_ - 1}</code> to serve as a shorthand for <code>(lambda [x] {x - 1})</code>, but I haven’t yet decided if I like the tradeoffs involved.</p><p>It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in <code>#lang racket</code> without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.</p><p>Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to <code>#lang hackett</code>, there’s no reason something similar but more powerful couldn’t be built as a separate library by a <em>user</em> of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.</p><ol class="footnotes"></ol></article>Simple, safe multimethods in Racket2016-02-18T00:00:00Z2016-02-18T00:00:00ZAlexis King<article><p>Racket ships with <code>racket/generic</code>, a system for defining <em>generic methods</em>, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports <em>single dispatch</em>. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.</p><h2><a name="motivating-multiple-dispatch"></a>Motivating multiple dispatch</h2><p>What is multiple dispatch and why is it necessary? Well, in most cases, it <em>isn’t</em> necessary at all. <a href="http://dl.acm.org/citation.cfm?doid=1449764.1449808">It has been shown that multiple dispatch is much rarer than single dispatch in practice.</a> However, when actually needed, having multiple dispatch in the toolbox is a valuable asset.</p><p>A classic example of multiple dispatch is multiplication over both scalars and vectors. Ideally, all of the following operations should work:</p><pre><code>2 × 3 = 6 +2 × ⟨3, 4⟩ = ⟨6, 8⟩ +⟨3, 4⟩ × 2 = ⟨6, 8⟩ +</code></pre><p>In practice, most languages do not support such flexible dispatch rules without fairly complicated branching constructs to handle each permutation of input types. Furthermore, since most languages only support single dispatch (such as most object-oriented languages), it is nearly impossible to add support for a new combination of types to an existing method.</p><p>To illustrate the above, even if a language supported operator overloading <em>and</em> it included a <code>Vector</code> class that overloaded multiplication to properly work with numbers and vectors, it might not implement matrix multiplication. If a user defines a <code>Matrix</code> class, they may overload <em>its</em> multiplication to support numbers, vectors, and matrices, but it is impossible to extend the multiplication implementation for the <code>Vector</code> class. That method is now completely set in stone, unless it is edited directly (and the programmer may not have access to <code>Vector</code>’s implementation).</p><p>Multiple dispatch solves all of these problems. Rather than specify implementations of functions for singular types, it is possible to specify implementations for sets of types. In the above example, a programmer would be able to define a new function that operates on <code>Vector</code> and <code>Matrix</code> arguments. Since each definition does not “belong” to any given type, extending this set of operations is trivial.</p><h2><a name="multiple-dispatch-in-racket"></a>Multiple dispatch in Racket</h2><p>This blog post is somewhat long and technical, so before proceeding any further, I want to show some real code that actually works so you can get a feel for what I’m talking about. As a proof-of-concept, I have created <a href="https://github.com/lexi-lambda/racket-multimethod">a very simple implementation of multiple dispatch in Racket</a>. The above example would look like this in Racket using my module:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Pardon the somewhat clunky syntax, but the functionality is there. Using the above code works as expected:</p><pre><code>&gt; (mul (num 2) (num 3)) +(num 6) +&gt; (mul (num 2) (vec '(3 4))) +(vec '(6 8)) +&gt; (mul (vec '(3 4)) (num 2)) +(vec '(6 8)) +</code></pre><p>Making the above snippet work is not particularly hard. In fact, it’s likely that most competent Racketeers could do it without much thought. However, there’s a tiny bit more going on behind the scenes than it may seem.</p><h2><a name="the-problem-with-multiple-dispatch"></a>The problem with multiple dispatch</h2><p>The single-dispatch design limitation of <code>racket/generic</code> comes directly from a desire to avoid what has been described as “spooky action at a distance”, a problem that is prevalent in many systems that support methods with multiple dispatch (aka <em>multimethods</em>). Specifically, the issue arises when new method implementations are defined for existing datatypes, which can have far-reaching effects throughout a program because the method table is global state. Both CLOS and Clojure suffer from this shortcoming.</p><p>Interestingly, Haskell with multi-parameter typeclasses (a nonstandard but highly useful extension) makes it quite trivial to create constructs similar to multiple dispatch (though the overload resolution is done at compile-time). The similarities are significant: Haskell <em>also</em> suffers from the possibility of a certain sort of “spooky action”. However, Haskell’s static typing and resolution allows the compiler to catch these potential issues, known as “orphan instances”, at compile time. Even though Racket does not support the same sort of static typing, the same idea can be used to keep multiple dispatch safe using the macro system.</p><h2><a name="safe-dynamically-typed-multiple-dispatch"></a>Safe, dynamically-typed multiple dispatch</h2><p>In order to make multiple dispatch safe, we first need to determine exactly what is unsafe. Haskell has rules for determining what constitutes an “orphan instance”, and these rules are equally applicable for determining dangerous multimethod implementations. Specifically, a definition can be considered unsafe if <em>both</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented was declared in a different module from the implementation.</p></li><li><p><em>All</em> of the types used for dispatch in the multimethod instance were declared in a different module from the implementation.</p></li></ol><p>Conversely, a multimethod implementation is safe if <em>either</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></li><li><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></li></ol><p>Why do these two rules provide a strong enough guarantee to eliminate the dangers created by global state? Well, to understand that, we need to understand what can go wrong if these rules are ignored.</p><h3><a name="multimethods-and-dangerous-instances"></a>Multimethods and dangerous instances</h3><p>What exactly is this dangerous-sounding “spooky action”, and what causes it? Well, the trouble stems from the side-effectful nature of multimethod instance definitions. Consider the Racket module from earlier, which defines multiplication instances for scalars and vectors:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Note that there is not actually a <code>(mul vec vec)</code> implementation. This is intentional: there are <em>two</em> ways to take the product of two vectors, so no default implementation is provided. However, it is possible that another module might desire an instance for <code>mul</code> that takes the dot product, and the programmer might write the following definition:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">foldl</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">y</span><span class="p">)))))</span></code></pre><p>However, there is something fishy about the above definition: it doesn’t need to be exported with <code>provide</code> to work! Since instances don’t create new bindings, they only add dispatch options, they don’t ever need to <code>provide</code> anything. This is problematic, though: it means that a program could continue to happily compile <em>even if</em> the module containing the dot product instance was never loaded with <code>require</code>, but an attempt to multiply two vectors would fail at runtime, claiming that there was no <code>(mul vec vec)</code> implementation. This drastic change of behavior violates Racket programmers’ assumptions about the guarantees made by modules (<code>require</code> should not cause any side-effects if the module’s bindings are not used).</p><p>Of course, while this seems potentially unexpected, it is workable: just be careful to <code>require</code> modules containing instances. Unfortunately, it gets much worse—what if a different library defines <em>its own</em> <code>(mul vec vec)</code> instance? What if that instance takes the cross product instead? That library may function entirely properly on its own, but when loaded alongside the program that defines a dot product instance, it is impossible to determine which instance should be used where. Because <code>define-instance</code> operates by modifying the aforementioned global state, the implementations clash, and the two systems <em>cannot</em> continue to operate together as written.</p><p>This is pretty bad. Defining extra instances is a reasonable use-case for multiple dispatch, but if these instances can break <em>third-party code</em>, how can they be trusted? This sort of problem can make multiple dispatch difficult to reason about and even more difficult to trust.</p><h3><a name="what-determines-safety"></a>What determines safety?</h3><p>With those problems in mind, we can turn back to the two rules for <em>safe</em> multiple dispatch. How do they prevent the above issues? Well, let’s take them one at a time.</p><p>Remember that an instance can be unequivocally determined to be safe if either of the two conditions are true, so we can consider them entirely independently. The first one is simple—an instance is safe if the following condition holds:</p><blockquote><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></blockquote><p>This one is pretty obvious. It is impossible to create a “bad” instance of a method declared in the same module because it is impossible to import the method without also bringing in the instance. Furthermore, a conflicting instance cannot be defined at the place where the types themselves are defined because that would require a circular module dependency, which Racket does not permit.</p><p>With the above explanation in mind, the second condition should make sense, too:</p><blockquote><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></blockquote><p>The same argument for the first point holds for the second, but with the parties swapped. Again, it is impossible to use the instance without somehow requiring the module that defines the datatype itself, so the instance would always be required, anyway. The most interesting aspect of this condition is that it demonstrates that instances can be defined for existing datatypes (that are defined in other modules) just so long as <em>at least one</em> of the datatypes is defined in the same module. This continues to permit the important use-case of extending the interfaces of existing types.</p><h3><a name="encoding-the-safety-rules-into-racket-s-macro-system"></a>Encoding the safety rules into Racket’s macro system</h3><p>In order to keep track of which methods and instances are defined where, I leveraged a technique based on the one <a href="http://www.ccs.neu.edu/racket/pubs/scheme2007-ctf.pdf">used by Typed Racket to keep track of whether or not a typed identifier is used in a typed or untyped context</a>. However, instead of using a simple mutable boolean flag, I used a mutable <a href="http://docs.racket-lang.org/syntax/syntax-helpers.html#%28tech._identifier._set%29">free identifier set</a>, which keeps track of the identifiers within a given module that should be considered “privileged”.</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/id-set</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mark-id-as-privileged!</span> +<span class="w"> </span><span class="n">id-privileged?</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="p">(</span><span class="n">mutable-free-id-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-add!</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-member?</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span></code></pre><p>Making this work with <code>define-generic</code> is obvious: just invoke <code>mark-id-as-privileged!</code> on the method name to note that the method is “privileged” in the scope of the current module. Keeping track of privileged structs is similarly straightforward, though it is a little more devious: the <code>multimethod</code> module provides a custom <code>struct</code> macro that just expands to <code>struct</code> from <code>racket/base</code>, but adds privilege information.</p><p>The <code>define-instance</code> macro does all the heavy lifting to ensure that only privileged identifiers can be used in instance definitions. A simple check for the identifier annotations is performed before proceeding with macro expansion:</p><pre><code class="pygments"><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">types</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">types</span><span class="p">)))</span></code></pre><p>When the privilege checks fail, an error is raised:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">)))</span></code></pre><p>With the above safeguards in place, the dangerous dot product implementation from above <strong>would not be allowed</strong>. The checks manage to encode both of the safety rules into the macro system such that invalid instances will fail <em>at compile time</em>, preventing dangerous uses of multimethods from ever slipping by unnoticed.</p><h3><a name="actually-implementing-multiple-dispatch"></a>Actually implementing multiple dispatch</h3><p>The rest of the multimethod implementation is relatively straightforward and is not even particularly robust. If anything, it is the bare minimum of what would be needed to allow the safety mechanisms above to work. Lots of features that would likely be needed in a real implementation are not included, and graceful error handling is largely ignored.</p><p>Multimethods themselves are implemented as Racket <a href="http://docs.racket-lang.org/guide/proc-macros.html#%28tech._transformer._binding%29">transformer bindings</a> containing custom data, including a reference to the multimethod’s arity and dispatch table. The custom datatype includes a <code>prop:procedure</code> structure type property, which allows such bindings to also function as macros. The macro procedure expands to an operation that looks up the proper instance to use in the multimethod’s dispatch table and invokes it with the supplied arguments.</p><p>The relevant code for defining multimethods is reproduced below:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="n">arity</span><span class="w"> </span><span class="n">dispatch-table</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="n">method</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">method</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="n">args</span><span class="p">))]))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-generic</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method:id</span><span class="w"> </span><span class="n">arg:id</span><span class="w"> </span><span class="n">...+</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">arity</span><span class="w"> </span><span class="p">(</span><span class="nb">length</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">arg</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="nb">make-hash</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod</span><span class="w"> </span><span class="n">arity</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">dispatch-table</span><span class="p">))))]))</span></code></pre><p>The dispatch tables are implemented entirely in terms of Racket’s structure types, so while they can be defined on arbitrary structure types (including ones defined in the Racket standard library), they <em>cannot</em> be defined on primitives such as pairs or vectors. Implementations are registered in the dispatch table using the compile-time information associated with structs’ transformer bindings, and the same information is retrieved from struct instances at runtime to look up the proper implementation to call. Notably, this only works if the struct is <code>#:transparent</code>, or more generally and accurately, if the calling code has access to the struct’s inspector. All structs defined by the <code>struct</code> form from the <code>multimethod</code> module are automatically marked as <code>#:transparent</code>.</p><p>The following code implements defining multimethod instances:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-instance</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="c1">; standard (define (proc ...) ...) shorthand</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">((</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; full (define proc lambda-expr) notation</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="n">proc:expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod-dispatch-table</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">compose1</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="n">extract-struct-info</span><span class="w"> </span><span class="nb">syntax-local-value</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))])</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">struct-types</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">hash-set!</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="n">struct-types</span><span class="w"> </span><span class="n">proc</span><span class="p">))))]))</span></code></pre><p>The resulting implementation is a useful, if certainly incomplete implementation of multimethods in Racket that does not sacrifice the safety provided by <code>racket/generic</code>’s single-dispatch approach.</p><h2><a name="related-work-advantages-and-disadvantages-and-areas-for-future-improvement"></a>Related work, advantages and disadvantages, and areas for future improvement</h2><p>As previously mentioned, this implementation of multiple dispatch was inspired by the types of APIs offered by CLOS and Clojure while also maintaining the safety of <code>racket/generic</code>. The inspiration for the safety rules came from GHC’s detection of orphan instances. Although most of the ideas presented above exist in other places, I am unsure if the concept of safety checking has been used before in any dynamically-typed programming languages.</p><p>The primary advantage offered over Racket’s existing generics system is obvious: multiple dispatch. Furthermore, this system can supersede many uses of <code>racket/generic</code> simply by dispatching on a single type. However, the current implementation does <em>not</em> support all of the features of <code>racket/generic</code>, such as supporting non-structure types and allowing fallback implementations. While those are well within the realm of possibility, other things like attaching structure type properties are probably not possible with this approach, so it is unlikely that the existing system could be subsumed by one like this one.</p><p>Additionally, this implementation would almost certainly need numerous improvements before being useful to most programmers:</p><ul><li><p><strong>Good error reporting for failure cases.</strong> Right now, even something obvious like calling a method on values that do not implement it simply fails with an error produced by <code>hash-ref</code>. In a more interesting sense, using the arity to generate compile-time error messages for <code>define-instance</code> would be a nice improvement.</p></li><li><p><strong>Support for Racket primitive data types.</strong> This might require some cooperation from Racket itself to permit an elegant implementation, but they could also just be special-cased. So long as lookup for primitives was done <em>after</em> consulting the main dispatch table, there wouldn’t be any performance hit for non-primitive types.</p></li><li><p><strong>Option to supply fallback implementations.</strong> This wouldn’t be too hard at all, though it’s questionable whether or not it would be useful without method groupings like <code>define/generic</code> provides. There would likely also need to be some sort of way to check if a set of values implements a particular method.</p></li><li><p><strong>Better cooperation with structure inspectors to alleviate the need for all structures to be transparent.</strong> It’s currently unclear to me how exactly this works and how it <em>should</em> work. There might be a better way to do this without mucking with inspectors.</p></li><li><p><strong>Much more flexible argument lists, including the ability to specify arguments that are not used for dispatch.</strong> This is really a pretty fundamental requirement, but the parsing required was significant enough for me to put it off for this initial prototype.</p></li><li><p><strong>Scribble forms to document generic methods and their instances.</strong> This is something <code>racket/generic</code> <em>doesn’t</em> have, and it has suffered for it. It would be very nice to have easy documentation forms for multimethods.</p></li><li><p><strong>Proper consideration of struct subtyping.</strong> Racket structs support subtyping, which I have not given much thought for this prototype. It is possible that subtyping violates constraints I had assumed would hold, so reviewing the existing code with that context would be useful.</p></li></ul><p>I’m not sure how much effort is involved in most of the above ideas, and in fact I’m not even completely sure how useful this system is to begin with. I have not found myself reaching much for multiple dispatch in my time as a Racket programmer, but that could simply be because it was previously unavailable. It will be interesting to see if that changes now that I have built this system, even if it is a bit rough around the edges.</p><h2><a name="conclusion"></a>Conclusion</h2><p>Despite the lack of need for multiple dispatch to solve most problems, as indicated by its general lack of support in mainstream programming languages, it’s a nice tool to have in the toolbox, and it <em>is</em> asked for in the Racket community from time to time (perhaps due to its familiarity in other parts of the Lisp world). Time will tell if pointing people to something like this will create or stifle interest in multiple dispatch for Racket.</p><p>The source for the <a href="https://github.com/lexi-lambda/racket-multimethod"><code>multimethod</code> package can be found here</a> if you are at all interested in playing with it yourself.</p><ol class="footnotes"></ol></article>ADTs in Typed Racket with macros2015-12-21T00:00:00Z2015-12-21T00:00:00ZAlexis King<article><p>Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate <em>why</em> macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is <strong>ADTs</strong>.</p><h2><a name="warning-this-is-not-a-macro-tutorial"></a>Warning: this is not a macro tutorial</h2><p>First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you <em>are</em> interested in learning about macros, I must recommend Greg Hendershott's <a href="http://www.greghendershott.com/fear-of-macros/">Fear of Macros</a>. It is good. This is not that.</p><p>Now, with that out of the way, let's get started.</p><h2><a name="what-we-re-building"></a>What we&rsquo;re building</h2><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data types</a>, or <em>ADTs</em>, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.</p><p>Racket also already has a facility for creating custom data structures in the form of <em>structs</em>, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.</p><p>With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="kt">Tree</span></code></pre><p>This already demonstrates a few of the core things we'll need to build:</p><ol><li><p>Each ADT has a <em>data type</em>, in this case <code>Tree</code>. This name only exists in the world of types, it isn't a value.</p></li><li><p>Each ADT has various <em>data constructors</em>, in this case <code>Leaf</code> and <code>Node</code>.</p></li><li><p>Each data constructor may accept any number of arguments, each of which have a specific type.</p></li><li><p>The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.</p></li></ol><p>Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>With this in mind, we can add a fifth and final point to our list:</p><ol start="5"><li><p>ADTs must be able to be parametrically polymorphic.</p></li></ol><p>That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.</p><h3><a name="describing-adts-in-racket"></a>Describing ADTs in Racket</h3><p>How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>This looks pretty good. Just like with the Haskell implementation, <code>Tree</code> should only exist at the type level, and <code>Empty</code>, <code>Leaf</code>, and <code>Node</code> should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be <code>(Leaf 7)</code>.</p><p>Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's <code>match</code>[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">tree-sum</span><span class="w"> </span><span class="p">((</span><span class="n">Tree</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">tree</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">tree</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Node</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">l</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">r</span><span class="p">))]))</span></code></pre><p>Given that Racket's <code>struct</code> form automatically produces identifiers that cooperate with <code>match</code>, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.</p><h2><a name="implementing-adts-as-syntax"></a>Implementing ADTs as syntax</h2><p>Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. The <code>syntax/parse</code> library works like the traditional Scheme <code>syntax-case</code> on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.</p><p>Since this is not a macro tutorial, the following implementation assumes you already know how to use <code>syntax/parse</code>. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.</p><h3><a name="parsing-types-with-a-syntax-class"></a>Parsing types with a syntax class</h3><p>To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like <code>Tree</code>, or they can be identifiers with parameters, like <code>(Tree a)</code>. We'll want to cover both cases.</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">))))</span></code></pre><p>This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (<code>...+</code>) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use <code>#:attr</code> to give the <code>param</code> attribute the default value of an empty list, so this syntax class will actually <em>normalize</em> the input we get in addition to actually parsing it.</p><h3><a name="a-first-attempt-at-define-datatype"></a>A first attempt at <code>define-datatype</code></h3><p>Now we can move on to actually implementing <code>define-datatype</code>. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using <code>syntax-parser</code>, which actually does the parsing for our macro.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">]))</span></code></pre><p>This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with <code>_</code> (which will just be the identifier <code>define-datatype</code>), then expecting a <code>type-name</code>, which uses the <code>type</code> syntax class we defined above. Next, we expect zero or more <code>data-constructor</code>s, which also use the <code>type</code> syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.</p><p>Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">([</span><span class="n">f</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))]))</span></code></pre><p>This is actually really close to being correct. This will generate a struct definition for each <code>data-constructor</code>, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have <em>names</em>, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for <em>all</em> the fields, <code>f</code>, so if any data constructor has two or more fields, we'll get an error.</p><p>Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called <code>generate-temporary</code>, which generates random identifiers. Our next attempt might look like this:</p><pre><code class="pygments"><span class="o">#`</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="o">#,</span><span class="p">(</span><span class="n">generate-temporary</span><span class="p">)</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>The <code>#,</code> lets us "escape" from the template to execute <code>(generate-temporary)</code> and interpolate its result into the syntax. Unfortunately, this doesn't work. We <em>do</em> generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.</p><h3><a name="more-leveraging-syntax-classes"></a>More leveraging syntax classes</h3><p>As it turns out, this is <em>also</em> easy to do with syntax classes. We can add an extra attribute to our <code>type</code> syntax class to generate a random identifier with each one. Again, we can use <code>#:attr</code> to do that automatically. Our new definition for <code>type</code> will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span></code></pre><p>Here we're using <code>generate-temporaries</code> instead of <code>generate-temporary</code>, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each <code>param</code>.</p><p>We can now fix our macro to use this <code>field-id</code> attribute instead of the static field name:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><h3><a name="creating-the-supertype"></a>Creating the supertype</h3><p>We're almost done—now we just need to implement our overall type, the one defined by <code>type-name</code>. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">Tree</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">Leaf</span><span class="w"> </span><span class="n">Node</span><span class="p">))</span></code></pre><p>However, a polymorphic type alias would need to include the type parameters in each subtype, like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using <strong>procedural macros</strong>.</p><p>To build each properly-instantiated type, we'll use a combination of <code>define/with-syntax</code> and Racket's list comprehensions, <code>for/list</code>. The <code>define/with-syntax</code> form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by <code>syntax-parser</code>. This will allow us to break up our result into multiple steps. Technically, <code>define/with-syntax</code> is not strictly necessary—we could just use <code>#`</code> and <code>#,</code>—but it's cleaner to work with.</p><p>We'll start by defining a set of instantiated data constructor types, one per <code>data-constructor</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span></code></pre><p>Now with our definition for <code>data-type</code>, we can implement our type alias for the supertype extremely easily:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><h3><a name="putting-it-all-together"></a>Putting it all together</h3><p>There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by <code>type-name</code> are in scope for each data constructor's structure definition. We can do this by making use of <code>type-name.param</code> within each produced struct definition, resulting in this:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><p>And we're done! The final macro, now completed, looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span> + +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span><span class="w"> </span><span class="k">...</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">)))]))</span></code></pre><p>It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.</p><h2><a name="using-our-adts"></a>Using our ADTs</h2><p>With the macro built, we can now actually use our ADTs using the syntax we described! The following is now <em>valid code</em>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span> +<span class="nb">-</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">Positive-Byte</span><span class="p">)</span> +<span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span></code></pre><p>We can use this to define common data types, such as Haskell's <code>Maybe</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Nothing</span><span class="p">)</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-default</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-default</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-then</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-then</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Nothing</span><span class="p">)]))</span></code></pre><p>And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="n">Expr</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="n">Number</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Expr</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Number</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">e</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Value</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Add</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">-</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Divide</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">/</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">7</span><span class="p">))))</span> +<span class="mi">4</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, <a href="https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e">I've put together a gist here</a>.</p><h2><a name="conclusions-and-credit"></a>Conclusions and credit</h2><p>This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly <em>are</em> powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.</p><p>This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.</p><p>That said, I think it's pretty cool.</p><p>Finally, I must give credit where credit is due. Thanks to <a href="http://andmkent.com">Andrew M. Kent</a> for the creation of the <a href="https://github.com/andmkent/datatype">datatype</a> package, which served as the inspiration for this blog post. Many thanks to <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> and <a href="http://www.ccs.neu.edu/home/matthias/">Matthias Felleisen</a> for their work on creating <code>syntax/parse</code>, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.</p><p>Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/macros.rss.xml b/feeds/macros.rss.xml new file mode 100644 index 0000000..030d5ea --- /dev/null +++ b/feeds/macros.rss.xml @@ -0,0 +1,1315 @@ +Posts tagged ‘macros’ | Alexis King’s BlogPosts tagged ‘macros’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/macros.html21 Apr 201921 Apr 201960Defeating Racket’s separate compilation guaranteehttps://lexi-lambda.github.io/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/https://lexi-lambda.github.io/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/21 Apr 2019<article><p>Being a self-described <a href="https://felleisen.org/matthias/manifesto/sec_pl-pl.html">programming-language programming language</a> is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate <a href="https://www.cs.utah.edu/plt/publications/macromod.pdf">composable and compilable macros</a>. One of the module system’s foundational properties is its <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29"><em>separate compilation guarantee</em></a>, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is <em>essential</em> for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.</p><p>Yet today, in this blog post, we’re going to do exactly that.</p><h2><a name="what-is-the-separate-compilation-guarantee"></a>What is the separate compilation guarantee?</h2><p>Before we get to the fun part (i.e. breaking things), let’s go over some fundamentals so we understand what we’re breaking. The authoritative source for the separate compilation guarantee is <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">the Racket reference</a>, but it is dense, as authoritative sources tend to be. Although I enjoy reading technical manuals for sport, it is my understanding that not all the people who read this blog are as strange as I am, so let’s start with a quick primer, instead. (If you’re already an expert, feel free to <a href="#section:main-start">skip to the next section</a>.)</p><p>Racket is a macro-enabled programming language. In Racket, a macro is a user-defined, code-to-code transformation that occurs at compile-time. These transformations cannot make arbitrary changes to the program—in Racket, they are usually required to be <em>local</em>, affecting a single expression or definition at a time—but they may be implemented using arbitrary code. This means that a macro can, if it so desires, read the SSH keys off your filesystem and issue an HTTP request to send them someplace.</p><p>That kind of attack is bad, admittedly, but it’s also <em>uninteresting</em>: Racket allows you do all that and then some, making no attempt to prevent it.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Racket calls these “external effects,” things that affect state outside of the programming language. They sound scary, but in practice, <em>internal effects</em>—effects that mutate state inside the programming language—are a much bigger obstacle to practical programming. Let’s take a look at why.</p><p>Let’s say we have a module with some global, mutable state. Perhaps it is used to keep track of a set of delicious foods:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><p>Using this interface, let’s write a program that checks if a particular food, given as a command-line argument, is delicious:</p><pre><code class="pygments"><span class="c1">;; check-food.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"pineapple"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"sushi"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"cheesecake"</span><span class="p">)</span> + +<span class="p">(</span><span class="k">command-line</span> +<span class="w"> </span><span class="kd">#:args</span><span class="w"> </span><span class="p">[</span><span class="n">food-to-check</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is not delicious.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>cheesecake +cheesecake<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>licorice +licorice<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>Exhilarating. (Sorry, licorice fans.) But what if a <em>macro</em> were to call <code>add-delicious-food!</code>? What would happen? For example, what if we wrote a macro to add a lot of foods at once?<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="n">fst:string</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd:string</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">for*</span><span class="w"> </span><span class="p">([</span><span class="n">fst-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">fst</span><span class="w"> </span><span class="k">...</span><span class="p">]))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">snd</span><span class="w"> </span><span class="k">...</span><span class="p">]))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="p">(</span><span class="nb">string-append</span><span class="w"> </span><span class="n">fst-str</span><span class="w"> </span><span class="s2">" "</span><span class="w"> </span><span class="n">snd-str</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">))</span> + +<span class="c1">; should add “fried chicken,” “roasted chicken”, “fried potato,” and “roasted potato”</span> +<span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="s2">"fried"</span><span class="w"> </span><span class="s2">"roasted"</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="s2">"chicken"</span><span class="w"> </span><span class="s2">"potato"</span><span class="p">])</span></code></pre><p>Now, what do you think executing <code>racket check-food.rkt 'fried chicken'</code> will do?</p><p>Clearly, the program should print <code>fried chicken is a delicious food</code>, and indeed, many traditional Lisp systems would happily produce such a result. After all, running <code>racket check-food.rkt 'fried chicken'</code> must load the source code inside <code>check-food.rkt</code>, expand and compile it, then run the result. While the program is being expanded, the compile-time calls to <code>add-delicious-food!</code> should add new elements to the <code>delicious-food</code> set, so when the program is executed, the string <code>"fried chicken"</code> ought to be in it.</p><p>But if you actually try this yourself, you will find that <em>isn’t</em> what happens. Instead, Racket rejects the program:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +check-food.rkt:12:11:<span class="w"> </span>add-delicious-food!:<span class="w"> </span>reference<span class="w"> </span>to<span class="w"> </span>an<span class="w"> </span>unbound<span class="w"> </span>identifier +<span class="w"> </span>at<span class="w"> </span>phase:<span class="w"> </span><span class="m">1</span><span class="p">;</span><span class="w"> </span>the<span class="w"> </span>transformer<span class="w"> </span>environment +<span class="w"> </span><span class="k">in</span>:<span class="w"> </span>add-delicious-food!</code></pre><p>Why does Racket reject this program? Well, consider that Racket allows programs to be pre-compiled using <code>raco make</code>, doing all the work of macroexpansion and compilation to bytecode ahead of time. Subsequent runs of the program will use the pre-compiled version, without having to run all the macros again. This is a problem, since expanding the <code>add-food-combinations!</code> macro had side-effects that our program depended on!</p><p>If Racket allowed the above program, it might do different things depending on whether it was pre-compiled. Running directly from source code might treat <code>'fried chicken'</code> as a delicious food, while running from pre-compiled bytecode might not. Racket considers this unacceptable, so it disallows the program entirely.</p><h3><a name="preserving-separate-compilation-via-phases"></a>Preserving separate compilation via phases</h3><p>Hopefully, you are now mostly convinced that the above program is a bad one, but you might have some lingering doubts. You might, for example, wonder if Racket disallows mutable compile-time state entirely. That is not the case—Racket really does allow everything that happens at runtime to happen at compile-time—but it does prevent compile-time and run-time state from ever <em>interacting</em>. Racket stratifies every program into a compile-time part and a run-time part, and it restricts communication between them to limited, well-defined channels (mainly via expanding to code that does something at run-time).</p><p>Racket calls this system of stratification <em>phases</em>. Code that executes at run-time belongs to the <em>run-time phase</em>, while code that executes at compile-time (i.e. macros) belongs to the <em>compile-time phase</em>. When a variable is defined, it is always defined in a particular phase, so bindings declared with <code>define</code> can only be used at run-time, while bindings declared with <code>define-for-syntax</code> can only be used at compile-time. Since <code>add-delicious-food!</code> was declared using <code>define</code>, it was not allowed (and in fact was not even visible) in the body of the <code>add-food-combinations!</code> macro.</p><p>While the whole macro system could work precisely as just described, such a strict stratification would be incredibly rigid. Since every definition would belong to either run-time or compile-time, but never both, reusing run-time code to implement macros would be impossible. While the example in the previous section might make it seem like that’s a good thing, it very often isn’t: imagine if general-purpose functions like <code>map</code> and <code>filter</code> all needed to be written twice!</p><p>To avoid this problem, Racket allows modules to be imported at both run-time and compile-time, so long as it’s done explicitly. Writing <code>(require "some-library.rkt")</code> requires <code>some-library.rkt</code> for run-time code, but writing <code>(require (for-syntax "some-library.rkt"))</code> requires it for compile-time code. Requiring a module <code>for-syntax</code> is sort of like implicitly adjusting all of its uses of <code>define</code> to be <code>define-for-syntax</code>, instead, effectively shifting all the code from run-time to compile-time. This kind of operation is therefore known as <em>phase shifting</em> in Racket terminology.</p><p>We can use phase shifting to make the program we wrote compile. If we adjust the <code>require</code> at the beginning of our program, then we can ensure <code>add-delicious-food!</code> is visible to both the run-time and compile-time parts of <code>check-food.rkt</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">))</span></code></pre><p>Now our program compiles. However, if you’ve been following everything carefully, you should be wondering why! According to the last section, sharing state between run-time and compile-time fundamentally can’t work without introducing inconsistencies between uncompiled and pre-compiled code. And that’s true—such a thing would cause all sorts of problems, and Racket doesn’t allow it. If you run the program, whether pre-compiled or not, you’ll find it always does the same thing:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>This seems rather confusing. What happened to the calls to <code>add-delicious-food!</code> inside our <code>add-food-combinations!</code> macro? If we stick a <code>printf</code> inside <code>add-delicious-food!</code>, we’ll find that it really does get called:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"Registering ~a as a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And in fact, if we pre-compile <code>check-food.rkt</code>, we’ll see that the first four registrations appear at compile-time, exactly as we expect:</p><pre><code class="pygments">$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>The compile-time registrations really are happening, but Racket is automatically restricting the compile-time side-effects so they only apply at compile-time. After compilation has finished, Racket ensures that compile-time side effects are thrown away, and the run-time code starts over with fresh, untouched state. This guarantees consistent behavior, since it becomes impossible to distinguish at run-time whether a module was just compiled on the fly, or if it was pre-compiled long ago (possibly even on someone else’s computer).</p><p>This is the essence of the separate compilation guarantee. To summarize:</p><ul><li><p>Run-time and compile-time are distinct <em>phases</em> of execution, which cannot interact.</p></li><li><p>Modules can be required at multiple phases via <em>phase shifting</em>, but their state is kept separate. Each phase gets its own copy of the state.</p></li><li><p>Ensuring that the state is kept separate ensures predictable program behavior, no matter when the program is compiled.</p></li></ul><p>This summary is a simplification of phases in Racket. The full Racket module system does not have only two phases, since macros can also be <em>used</em> at compile-time to implement other macros, effectively creating a separate “compile-time” for the compile-time code. Each compile-time pass is isolated to its own phase, creating a finite but arbitrarily large number of distinct program phases (all but one of which occur at compile-time).</p><p>Furthermore, the separate compilation guarantee does not just isolate the state of each phase from the state of other phases but also isolates all compile-time state from the compile-time state of other modules. This ensures that compilation is still deterministic even if modules are compiled in a different <em>order</em>, or if several modules are sometimes compiled individually while other times compiled together all at once.</p><p>If you want to learn more, the full details of the module system are described at length in the <a href="https://docs.racket-lang.org/guide/phases.html">General Phase Levels</a> section of the Racket Guide, but the abridged summary I’ve described is enough for the purposes of this blog post. If the bulleted list above mostly made sense to you, you’re ready to move on.</p><h2 id="section:main-start">How we’re going to break it</h2><p>The separate compilation guarantee is a sturdy opponent, but it is not without weaknesses. Although no API in Racket, safe or unsafe, allows arbitrarily disabling phase separation, a couple features of Racket are already known to allow limited forms of cross-phase communication.</p><p>The most significant of these, and the one we’ll be using as our vector of attack, is the logger. Unlike many logging systems, which are exclusively string-oriented, Racket’s logging interface allows structured logging by associating an arbitrary Racket value with each and every log message. Since it is possible to set up listeners within Racket that receive log messages sent to a particular “topic,” the logger can be used as a communication channel to send values between different parts of a program.</p><p>The following program illustrates how this works. One thread creates a listener for all log messages on the topic <code>'send-me-a-value</code> using <code>make-log-receiver</code>, then uses <code>sync</code> to block until a value is received. Meanwhile, a second thread sends values through the logger using <code>log-message</code>. Together, this creates a makeshift buffered, asynchronous channel:</p><pre><code class="pygments"><span class="c1">;; log-comm.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span><span class="w"> </span><span class="c1">; wait forever</span></code></pre><pre><code>$ racket log-comm.rkt +'#(debug "" 1 send-me-a-value) +'#(debug "" 2 send-me-a-value) +'#(debug "" 3 send-me-a-value) +'#(debug "" 4 send-me-a-value) +^Cuser break +</code></pre><p>In this program, the value being sent through the logger is just a number, which isn’t very interesting. But the value really can be <em>any</em> value, even arbitrary closures or mutable data structures. It’s even possible to send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> through a logger, which can subsequently be used to communicate directly, without having to abuse the logger.</p><p>Generally, this feature of loggers isn’t very useful, since Racket has plenty of features for cross-thread communication. What’s special about the logger, however, is that it is global, and it is cross-phase.</p><p>The cross-phase nature of the logger makes some sense. If a Racket program creates a namespace (that is, a fresh environment for dynamic evaluation), then uses it to expand and compile a Racket module, the process of compilation might produce some log messages, and the calling thread might wish to receive them. It wouldn’t be a very useful logging system if log messages during compile-time were always lost. However, this convenience is a loophole in the phase separation system, since it allows values to flow—bidirectionally—between phases.</p><p>This concept forms the foundation of our exploit, but it alone is not a new technique, and I did not discover it. However, all existing uses I know of that use the logger for cross-phase communication require control of the parent namespace in which modules are being compiled, which means some code must exist “outside” the actual program. That technique does not work for ordinary programs run directly with <code>racket</code> or compiled directly with <code>raco make</code>, so to get there, we’ll need something more clever.</p><h3><a name="the-challenge"></a>The challenge</h3><p>Our goal, therefore, is to share state between phases <em>without</em> controlling the compilation namespace. More precisely, we want to be able to create an arbitrary module-level definition that is <em>cross-phase persistent</em>, which means it will be evaluated once and only once no matter how many times its enclosing module is re-instantiated (i.e. given a fresh, untouched state) at various phases. A phase-shifted <code>require</code> of the module that contains the definition should share state with an unshifted version of the module, breaking the separate compilation guarantee wide open.</p><p>To use the example from the previous section, we should be able to adjust <code>foods.rkt</code> very slightly…</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="c1">; share across phases</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="cm">#| ... |#</span></code></pre><p>…and the <code>delicious-foods</code> mutable state should magically become cross-phase persistent. When running <code>check-food.rkt</code> from source, we should see the side-effects persisted from the module’s compilation, while running from pre-compiled bytecode should give us the result with compile-time effects discarded.</p><p>We already know the logger is going to be part of our exploit, but implementing <code>define/cross-phase</code> on top of it is more subtle than it might seem. In our previous example that used <code>make-log-receiver</code>, we had well-defined sender and receiver threads, but who is the “sender” in our multi-phased world? And what exactly is the sender sending?</p><p>To answer those questions, allow me to outline the general idea of our approach:</p><ol><li><p>The first time our <code>foods.rkt</code> module is instantiated, at any phase, it evaluates the <code>(mutable-set)</code> expression to produce a new mutable set. It spawns a sender thread that sends this value via the logger to anyone who will listen, and that thread lingers in the background for the remaining duration of the program.</p></li><li><p>All subsequent instantiations of <code>foods.rkt</code> do <em>not</em> evaluate the <code>(mutable-set)</code> expression. Instead, they obtain the existing set by creating a log receiver and obtaining the value the sender thread is broadcasting. This ensures that a single value is shared across all instantiations of the module.</p></li></ol><p>This sounds deceptively simple, but the crux of the problem is how to determine whether <code>foods.rkt</code> has previously been instantiated or not. Since we can only communicate across phases via the logger, we cannot use any shared state to directly record the first time the module is instantiated. We can listen to a log receiver and wait to see if we get a response, but this introduces a race condition: how long do we wait until giving up and deciding we’re the first instantiation? Worse, what if two threads instantiate the module at the same time, and both threads end up spawning a new sender thread, duplicating the state?</p><p>The true challenge, therefore, is to develop a protocol by which we can be <em>certain</em> we are the first instantiation of a module, without relying on any unspecified behavior, and without introducing any race conditions. This is possible, but it isn’t obvious, and it requires combining loggers with some extra tools available to the Racket programmer.</p><h3><a name="the-key-idea"></a>The key idea</h3><p>It’s finally time to tackle the key idea at the heart of our exploit: garbage collection. In Racket, garbage collection is an observable effect, since Racket allows attaching finalizers to values via <a href="https://docs.racket-lang.org/reference/willexecutor.html">wills and executors</a>. Since a single heap is necessarily shared by the entire VM, behavior happening on other threads (even in other phases) can be indirectly observed by creating a unique value—a “canary”—then sending it to another thread, and waiting to see if it will be garbage collected or not (that is, whether or not the canary “dies”).</p><p>Remember that logs and log receivers are effectively buffered, multicast, asynchronous FIFO channels. Since they are buffered, if any thread is already listening to a logger topic when a value is sent, it cannot possibly be garbage collected until that thread either reads it and discards it or the receiver itself is garbage collected. It’s possible to use this mechanism to observe whether or not another thread is already listening on a topic, as the following program demonstrates:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">executor</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><pre><code>$ racket check-receivers.rkt +no receivers for 'bar +receiver exists for 'foo +</code></pre><p>However, this program has some problems. For one, it needs to call <code>collect-garbage</code> several times to be certain that the canary will be collected if there are no listeners, which can take a second or two, and it also assumes that three calls to <code>collect-garbage</code> will be enough to collect the canary, though there is no guarantee that will be true.</p><p>A bulletproof solution should be both reasonably performant and guaranteed to work. To get there, we have to combine this idea with something more. Here’s the trick: instead of sending the canary alone, send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> alongside it. Synchronize on both the canary’s executor <em>and</em> the channel so that the thread will unblock if either the canary is collected <em>or</em> the channel is received and sent a value using <code>channel-put</code>. Have the receiver listen for the channel on a separate thread, and when it receives it, send a value back to unblock the waiting thread as quickly as possible, without needing to rely on a timeout or a particular number of calls to <code>collect-garbage</code>.</p><p>Using that idea, we can revise the program:</p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="c1">; send the channel + the canary</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">canary</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span><span class="w"> </span><span class="c1">; yield to try to let the receiver thread work</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">received</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">)))</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">received</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span><span class="w"> </span><span class="c1">; collect garbage and try again</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> +<span class="p">(</span><span class="nb">void</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="k">_</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><p>Now the program completes almost instantly. For this simple program, the explicit <code>(sleep)</code> call is effective enough at yielding that, on my machine, <code>(check-receivers 'foo)</code> returns without ever calling <code>collect-garbage</code>, and <code>(check-receivers 'bar)</code> returns after performing a single minor collection.</p><p>This is extremely close to a bulletproof solution, but there are two remaining subtle issues:</p><ol><li><p>There is technically a race condition between the <code>(sync recv)</code> in the receiver thread and the subsequent <code>channel-put</code>, since it’s possible for the canary to be received, discarded, and garbage collected before reaching the call to <code>channel-put</code>, which the sending thread would incorrectly interpret as indicating the topic has no receivers.</p><p>To fix that, the receiver thread can send the canary itself back through the channel, which fundamentally has to work, since the value cannot be collected until it has been received by the sending thread, at which point the <code>sync</code> has already chosen the channel.</p></li><li><p>It is possible for the receiver thread to receive the log message and call <code>channel-put</code>, but for the sending thread to somehow die in the meantime (which cannot be protected against in general in Racket, since <code>thread-kill</code> immediately and forcibly terminates a thread). If this were to happen, the sending thread would never obtain the value from the channel, blocking the receiving thread indefinitely.</p><p>A solution is to spawn a new thread for each <code>channel-put</code> instead of calling it directly from the receiving thread. Conveniently, this both ensures the receiving thread never gets stuck and avoids resource leaks, since the Racket runtime is smart enough to GC a thread blocked on a channel that has no other references and therefore can never be unblocked.</p></li></ol><p>With those fixes in place, the program is, to the best of my knowledge, bulletproof. It will always correctly determine whether or not a logger has a listener, with no race conditions or reliance upon unspecified behavior of the Racket runtime. It does, however, make a couple of assumptions.</p><p>First, it assumes that the value of <code>(current-logger)</code> is shared between the threads. It is true that <code>(current-logger)</code> can be changed, and sometimes is, but it’s usually done via <code>parameterize</code>, not mutation of the parameter directly. Therefore, this can largely be mitigated by storing the value of <code>(current-logger)</code> at module instantiation time.</p><p>Second, it assumes that no other receivers are listening on the same topic. Technically, even using a unique, uninterned key for the topic is insufficient to ensure that no receivers are listening to it, since a receiver can choose to listen to all topics. However, in practice, it is highly unlikely that any receiver would willfully choose to listen to all topics at the <code>'debug</code> level, since the receiver would be inundated with enormous amounts of useless information. Even if such a receiver were to be created, it is highly likely that it would dequeue the messages as quickly as possible and discard the accompanying payload, since doing otherwise would cause all messages to be retained in memory, leading to a significant memory leak.</p><p>Both these problems can be mitigated by using a logger other than the root logger, which is easy in this example. However, for the purpose of subverting the separate compilation guarantee, we would have no way to share the logger object itself across phases, defeating the whole purpose, so we are forced to use the root logger and hope the above two assumptions remain true (but they usually do).</p><h3><a name="preparing-the-exploit"></a>Preparing the exploit</h3><p>If you’ve made it here, congratulations! The most difficult part of this blog post is over. All that’s left is the fun part: performing the exploit.</p><p>The bulk of our implementation is a slightly adapted version of <code>check-receivers</code>:</p><pre><code class="pygments"><span class="c1">;; define-cross-phase.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="k">thunk</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="k">==</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="nb">eq?</span><span class="p">))</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">execute-evt</span><span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="nb">will-execute</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">execute-evt</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">result</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">value</span><span class="p">)</span> +<span class="w"> </span><span class="n">value</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="p">(</span><span class="k">thunk</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">value</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)]))))</span> +<span class="w"> </span><span class="n">value</span><span class="p">]))</span></code></pre><p>There are a few minor differences, which I’ll list:</p><ol><li><p>The most obvious difference is that <code>make-cross-phase</code> does the work of both checking if a receiver exists—which I’ll call the <em>manager thread</em>—and spawning it if it doesn’t. If it does end up spawning a manager thread, it evaluates the given thunk to produce a value, which becomes the cross-phase value that will be sent through the channel alongside the canary.</p></li><li><p>Once the manager thread is created, subsequent calls to <code>make-cross-phase</code> will receive the value through the channel and return it instead of re-invoking <code>thunk</code>. This is what ensures the right-hand side of each use of <code>define/cross-phase</code> is only ever evaluated once.</p></li><li><p>Since <code>make-cross-phase</code> needs to create a log receiver if no manager thread exists, it does so immediately, before sending the canary through the logger. This avoids a race condition between multiple threads that are simultaneously competing to become the manager thread, where both threads could send a canary through the logger before either was listening, both canaries would get GC’d, and both threads would spawn a new manager.<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><p>Creating the receiver before sending the canary avoids this problem, but the thread now needs to receive its own canary and discard it before synchronizing on the channel and executor, since otherwise it will retain a reference to the canary. It’s possible that in between creating the receiver and sending the canary, another thread also sent a canary, so it needs to drop any log messages it finds that don’t include its own canary.</p><p>This ends up working out perfectly, since every thread drops all the messages received before the one containing its own canary, but retains all subsequent values. This means that only one thread can ever “win” and become the manager, since the first thread to send a canary is guaranteed to retain all subsequent canaries, yet also guaranteed its canary will be GC’d. Other threads racing to become the manager will remain blocked until the manager thread is created, since its canaries will be retained by the manager-to-be until it dequeues them.</p><p>(This is the most subtle part of the process to get right, but conveniently, it mostly just works out without very much code. If you didn’t understand any of the above three paragraphs, it isn’t a big deal.)</p></li></ol><p>The final piece to this puzzle is to define the <code>define/cross-phase</code> macro that wraps <code>make-cross-phase</code>. The macro is actually slightly more involved than just generating a call to <code>make-cross-phase</code> directly, since we’d like to use an uninterned symbol for the topic instead of an interned one, just to ensure it is unique. Ordinarily, this might seem impossible, since an uninterned symbol is fundamentally a unique value that needs to be communicated across phases, and the whole problem we are solving is creating a communication channel that spans phases. However, Racket actually provides some built-in support for sharing uninterned symbols across phases (plus some other kinds of values, but they must always be immutable). To do this, we need to generate a <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._cross-phase._persistent-modules%29">cross-phase persistent submodule</a> that exports an uninterned symbol, then pass that symbol as the topic to <code>make-cross-phase</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">define/cross-phase</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">cross-phase-topic-key</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">#%kernel</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%declare</span><span class="w"> </span><span class="kd">#:cross-phase-persistent</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%provide</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">topic</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="s2">"cross-phase"</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">topic-mod-name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">e</span><span class="p">)))))</span></code></pre><p>And that’s really it. We’re done.</p><h3><a name="executing-the-exploit"></a>Executing the exploit</h3><p>With our implementation of <code>define/cross-phase</code> in hand, all that’s left to do is run our original <code>check-foods.rkt</code> program and see what happens:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +set-add!:<span class="w"> </span>contract<span class="w"> </span>violation: +expected:<span class="w"> </span>set? +given:<span class="w"> </span><span class="o">(</span>mutable-set<span class="w"> </span><span class="s2">"fried chicken"</span><span class="w"> </span><span class="s2">"roasted chicken"</span><span class="w"> </span><span class="s2">"roasted potato"</span><span class="w"> </span><span class="s2">"fried potato"</span><span class="o">)</span> +argument<span class="w"> </span>position:<span class="w"> </span>1st +other<span class="w"> </span>arguments...: +<span class="w"> </span>x:<span class="w"> </span><span class="s2">"pineapple"</span></code></pre><p>Well, I don’t know what you expected. Play stupid games, win stupid prizes.</p><p>This error actually makes sense, but it belies one reason (of many) why this whole endeavor is probably a bad idea. Although we’ve managed to make our mutable set cross-phase persistent, our references to set operations like <code>set-add!</code> and <code>set-member?</code> are not, and every time <code>racket/set</code> is instantiated in a fresh phase, it creates an entirely new instance of the <code>set</code> structure type. This means that even though we have a bona fide mutable set, it isn’t actually the type of set that this phase’s <code>set-add!</code> understands!</p><p>Of course, this isn’t a problem that some liberal application of <code>define/cross-phase</code> can’t solve:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-member?</span><span class="w"> </span><span class="nb">set-member?</span><span class="p">)</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-add!</span><span class="w"> </span><span class="nb">set-add!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And thus we find that another so-called “guarantee” isn’t.</p><h2><a name="reflection"></a>Reflection</h2><p>Now comes the time in the blog post when I have to step back and think about what I’ve done. Have mercy.</p><p>Everything in this blog post is a terrible idea. No, you should not use loggers for anything that isn’t logging, you shouldn’t use wills and executors for critical control flow, and obviously you should absolutely not intentionally break one of the most helpful guarantees the Racket module system affords you.</p><p>But I thought it was fun to do all that, anyway.</p><p>The meaningful takeaways from this blog post aren’t that the separate compilation guarantee can be broken, nor that any of the particular techniques I used hold, but that</p><ol><li><p>ensuring non-trivial guarantees is really hard,</p></li><li><p>despite that, the separate compilation guarantee is really, really hard to break,</p></li><li><p>the separate compilation guarantee is good, and you should appreciate the luxury it affords you while writing Racket macros,</p></li><li><p>avoiding races in a concurrent environment can be extremely subtle,</p></li><li><p>and Racket is totally <em>awesome</em> for giving me this much rope to hang myself with.</p></li></ol><p>If you want to hang yourself with Racket, too, <a href="https://gist.github.com/lexi-lambda/f173a84fc9727977bcea657b3bb0cd4f">runnable code from this blog post is available here</a>.</p><ol class="footnotes"><li id="footnote-1"><p>This isn’t <em>strictly</em> true, since Racket provides sandboxing mechanisms that can compile and execute untrusted code without file system or network access, but this is not the default compilation mode. Usually, it doesn’t matter nearly as much as it might sound: most of the time, if you’re compiling untrusted code, you’re also going to run it, and running untrusted code can do all those things, anyway. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>This is actually a <em>terrible</em> use case for a macro, since an ordinary function would do just fine, but I’m simplifying a little to keep the example small. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Racket actually provides this functionality directly via the <code>log-level?</code> procedure. However, since <code>log-level?</code> provides no way to determine how <em>many</em> receivers are listening to a topic, using it to guard against creating a receiver is vulnerable to a race condition that the garbage collection-based approach can avoid, as is discussed later. Furthermore, the GC technique is more likely to be resilient to nosy log receivers listening on all topics at the <code>'debug</code> level, since they will almost certainly dequeue and discard the value quickly (as otherwise they would leak large quantities of memory). <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>This race is the one that makes using <code>log-level?</code> untenable, since the receiver needs to be created before the topic is checked for listeners to avoid the race, which can’t be done with <code>log-level?</code> (since it would always return <code>#t</code>). <a href="#footnote-ref-4-1">↩</a></p></li></ol></article>Macroexpand anywhere with local-apply-transformer!https://lexi-lambda.github.io/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/https://lexi-lambda.github.io/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/06 Oct 2018<article><p>Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only <em>certain positions</em> in Racket code are subject to macroexpansion.</p><p>To illustrate, consider a macro that provides a Clojure-style <code>let</code> form:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This can be used anywhere an expression is expected, and it does as one would expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>However, a novice macro programmer might realize that <code>clj-let</code> really only modifies the syntax of <em>binding pairs</em> for a <code>let</code> form. Therefore, could one define a macro that only adjusts the binding pairs of some existing <code>let</code> form instead of expanding to an entire <code>let</code>? That is, could one write the above example like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>The answer is <em>no</em>: the binding pairs of a <code>let</code> form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion <em>anywhere</em> in a Racket program.</p><h2><a name="why-only-some-positions-are-subject-to-macroexpansion"></a>Why only some positions are subject to macroexpansion</h2><p>To understand <em>why</em> the macroexpander refuses to touch certain positions in a program, we must first understand how the macro system operates. In Racket, a macro is defined as a compile-time function associated with a particular binding, and macros are given complete control over the syntax trees they are surrounded with. If we define a macro <em><code>mac</code></em>, then we write the expression <code>(<em>mac</em> <em>form</em>)</code>, <em><code>form</code></em> is provided as-is to <em><code>mac</code></em> as a syntax object. Its structure can be anything at all, since <em><code>mac</code></em> can be an arbitrary Racket function, and that function can use <em><code>form</code></em> however it pleases.</p><p>To give a concrete illustration, consider a macro that binds some identifiers to symbols in a local scope:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">x</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="ss">hello</span><span class="w"> </span><span class="ss">goodbye</span><span class="p">)</span></code></pre><p>It isn’t the most exciting macro in the world, but it illustrates a key point: the first subform to <code>let-symbols</code> is a list of identifiers that are eventually put in <em>binding</em> position. This means that <code>hello</code> and <code>goodbye</code> are bindings, not uses, and such bindings shadow any existing bindings that might have been in scope:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">foo</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> +<span class="w"> </span><span class="n">foo</span><span class="p">))</span> +<span class="o">&#39;</span><span class="ss">foo</span></code></pre><p>This might not seem very interesting, but it’s critical to understand, since it means that the expander <em>can’t know</em> which sub-pieces of a use of <code>let-symbols</code> will eventually be expressions themselves until it expands the macro and discovers it produces a <code>let</code> form, so it can’t know where it’s safe to perform macroexpansion. To make this more explicit, imagine we define a macro under some name, then try and use that name with our <code>let-symbols</code> macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">x:id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="n">hello</span><span class="p">)</span></code></pre><p>What should the above program do? If we treat the first use of <code>hello</code> in the <code>let-symbols</code> form as a macro application, then <code>(hello goodbye)</code> should be transformed into <code>(goodbye)</code>, and the use of <code>hello</code> in the body should be a syntax error. But if the first use of <code>hello</code> was instead intended to be a binder, then it should shadow the <code>hello</code> definition above, and the output of the program should be <code>'hello</code>.</p><p>To avoid the chaos that would ensue if defining a macro could completely break local reasoning about other macros, Racket chooses the second option, and the program produces <code>'hello</code>. The macroexpander has no way of knowing <em>how</em> each macro will inspect its constituent pieces, so it avoids touching anything until the macro expands. After it discovers the <code>let</code> form in the expansion of <code>let-symbols</code>, it can safely determine that the body expressions are, indeed, expressions, and it can recursively expand any macros they contain. To put things another way, a macro’s sub-forms are never expanded before the macro itself is expanded, only after.</p><h2><a name="forcing-sub-form-expansion"></a>Forcing sub-form expansion</h2><p>The above section explains why the expander must operate as it does, but it’s a little bit unsatisfying. What if we write a macro where we <em>want</em> certain sub-forms to be expanded before they are passed to us? Fortunately, the Racket macro system provides an API to handle this use case, too.</p><p>It is true that the Racket macro system never <em>automatically</em> expands sub-forms before outer forms are expanded, but macro transformers can explicitly op-in to recursive expansion via the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a> function. This function effectively yields control back to the expander to expand some arbitrary piece of syntax as an expression, and when it returns, the macro transformer can inspect the expanded expression however it wishes. In theory, this can be used to implement extensible macros that allow macroexpansion in locations other than expression position.</p><p>To give an example of such a macro, consider the Racket <code>match</code> form, which implements an expressive pattern-matcher as a macro. One of the most interesting qualities of Racket’s <code>match</code> macro is that its pattern language is user-extensible, essentially allowing pattern-level macros. For example, a user might find they frequently match against natural numbers, and they wish to be able to write <code>(nat n)</code> as a shorthand for <code>(? exact-nonnegative-integer? n)</code>. Fortunately, this is easy using <code>define-match-expander</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-match-expander</span><span class="w"> </span><span class="n">nat</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">pat</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">?</span><span class="w"> </span><span class="nb">exact-nonnegative-integer?</span><span class="w"> </span><span class="n">pat</span><span class="p">)]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="mi">-5</span><span class="w"> </span><span class="mi">-2</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">-7</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">list</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">nat</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">n</span><span class="p">])</span> +<span class="mi">4</span></code></pre><p>Clearly, <code>match</code> is somehow expanding the <code>nat</code> match expander as a part of its expansion. Is it using <code>local-expand</code>?</p><p>Well, no. While <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">a previous blog post of mine</a> has illustrated that it is possible to do such a thing with <code>local-expand</code> via some clever trickery, <code>local-expand</code> is really designed to expand <em>expressions</em>. This is a problem, since <code>(nat n)</code> is not an expression, it’s a pattern: it will expand into <code>(? exact-nonnegative-integer? n)</code>, which will lead to a syntax error, since <code>?</code> is not bound in the world of expressions. Instead, for a long while, <code>match</code> and forms like it have emulated how the expander performs macroexpansion in ad-hoc ways. Fortunately, as of Racket v7.0, the new <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Fapply-transformer..rkt%29._local-apply-transformer%29%29"><code>local-apply-transformer</code></a> API provides a way to invoke recursive macroexpansion in a consistent way, and it doesn’t assume that what’s being expanded is an expression.</p><h3><a name="a-closer-look-at-local-apply-transformer"></a>A closer look at <code>local-apply-transformer</code></h3><p>If <code>local-apply-transformer</code> is the answer, what does it actually do? Well, <code>local-apply-transformer</code> allows explicitly invoking a transformer function on some piece of syntax and retrieving the result. In other words, <code>local-apply-transformer</code> allows expanding an arbitrary macro, but since it doesn’t make any assumptions about what the output will be, it only expands it <em>once</em>: just a single step of macro transformation.</p><p>To illustrate, we can write a macro that uses <code>local-apply-transformer</code> to invoke a transformer function and preserve the result using <code>quote-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/apply-transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define-for-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>When we use <code>mac</code>, our <code>flip</code> function will be applied, as a macro, to the syntax object we provide:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="nb">&gt;</span></code></pre><p>Alright, so this works, but it raises some questions. Why is <code>flip</code> defined as a function at phase 1 (using <code>define-for-syntax</code>) instead of as a macro (using <code>define-syntax</code>)? What’s the deal with the <code>'expression</code> argument to <code>local-apply-transformer</code> given that <code>local-apply-transformer</code> is supposedly decoupled from expression expansion? And finally, how is this any different from just calling our <code>flip</code> function on the syntax object directly by writing <code>(flip #'(([x 1]) let x))</code>?</p><p>Let’s start with the first of those questions: why is <code>flip</code> defined as a function rather than as a macro? Well, <code>local-apply-transformer</code> is a fairly low-level operation: remember, it doesn’t assume <em>anything</em> about the argument it’s given! Therefore, it doesn’t take an expression containing a macro and expand it based on its structure, it needs to be explicitly provided the macro transformer function to apply. In practice, this might not seem very useful, since presumably we want to write our macros as macros, not as phase 1 functions. Fortunately, it’s possible to look up the function associated with a macro binding using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, so if we use that, we can define <code>flip</code> using <code>define-syntax</code> as usual:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>Now for the next question: what is the meaning of the <code>'expression</code> argument? This one is more of a historical artifact than anything else: when the expander applies a macro transformer, it does it in a “context”, which is accessible using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-context%29%29"><code>syntax-local-context</code></a> function. This context can be one of a predefined enumeration of cases, including <code>'expression</code>, <code>'top-level</code>, <code>'module</code>, <code>'module-begin</code>, or a list representing a definition context. Whether or not any of those actually apply to our use case, we still have to pick one, but aside from how they affect the value returned by <code>syntax-local-context</code> (which some macros inspect), the value we choose is largely irrelevant. Using <code>'expression</code> will do, even if it’s a bit of a lie.</p><p>Finally, how does any of this differ from just applying the function we get directly? Well, the critical answer is all about <em>hygiene</em>. Racket’s macro system is hygienic, which, among other things, ensures bindings defined with the same name in different places do not unintentionally conflict. Racket’s hygiene mechanism is implemented in the macroexpander, when macro transformers are applied. If we just applied the <code>flip</code> transformer procedure to a syntax object directly, we would circumvent this hygiene mechanism, potentially causing all sorts of problems. By using <code>local-apply-transformer</code>, we ensure hygiene is preserved.</p><p>There is one small problem left with our program, however. Can you spot it? The key is to consider what would happen if we used <code>flip</code> as an ordinary macro, without using <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="n">let:</span><span class="w"> </span><span class="n">bad</span><span class="w"> </span><span class="k">syntax</span> +<span class="w"> </span><span class="n">in:</span><span class="w"> </span><span class="k">let</span></code></pre><p>What happened? Well, remember that when a macro in Racket is used, it receives the whole use site as a syntax object: in this case, <code>#'(flip (([x 1]) let x))</code>. This means that <code>flip</code> ought to be written to parse its argument slightly differently:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span></code></pre><p>Indeed, now that we’ve properly restructured the macro, we can easily switch to using the convenient <code>define-simple-macro</code> shorthand:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This means we also need to update our definition of <code>mac</code> to provide the full syntax object the expander would:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>This might seem redundant, but remember, <code>local-apply-transformer</code> is very low-level! While the convention that <code>(<em>mac</em> . _)</code> is the syntax for a macro transformation might seem obvious, <code>local-apply-transformer</code> makes no assumptions. It just does what we tell it to do.</p><h3><a name="applying-local-apply-transformer"></a>Applying <code>local-apply-transformer</code></h3><p>So what does <code>local-apply-transformer</code> have to do with the problem at the beginning of this blog post? Well, as it happens, we can use <code>local-apply-transformer</code> to implement a macro that allows expansion <em>anywhere</em> using some simple tricks. While it’s true that we cannot magically divine which locations ought to be expanded, what we <em>can</em> do is explicitly annotate which places to expand.</p><p>To do this, we will implement a macro, <code>expand-inside</code>, that looks for subforms annotated with a special <code>$expand</code> identifier and performs macro transformation on those locations before proceeding with ordinary macroexpansion. Using the <code>clj-binding-pairs</code> example from the beginning of this blog post, our solution to that problem will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Put another way, <code>expand-inside</code> will force eager expansion on any subform surrounded with an <code>$expand</code> annotation.</p><p>We’ll start by defining the <code>$expand</code> binding itself. This binding won’t mean anything at all outside of <code>expand-inside</code>, but we’d like it to be a unique binding so that users can rename it (using, <code>rename-in</code>, for example) if they wish. To do this, we’ll use the usual trick of defining it as a macro that always produces an error if it’s ever used:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"illegal outside an ‘expand-inside’ form"</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span></code></pre><p>Next, we’ll implement a syntax class that will form the bulk of our implementation of <code>expand-inside</code>. Since we need to find uses of <code>$expand</code> that might be deeply-nested inside the syntax object provided to <code>expand-inside</code>, we need to recursively look through the syntax object, find any instances of <code>$expand</code>, and put it all back together once we’re done. This can be done relatively cleanly using a recursive syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">do-expand-inside</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="n">$expand</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">$expand</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:do-expand-inside</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">a:do-expand-inside</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">b:do-expand-inside</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">reassembled</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">a.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">reassembled</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">reassembled</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>There are some tricky details to get right in the reassembly of pairs, since syntax lists are actually composed of ordinary pairs rather than syntax pairs, but ultimately, the code for walking a syntax object is small. The key case of this syntax class is the call to <code>do-$expand</code> in the first clause, which we have not yet defined. This function will actually handle performing the expansion by invoking <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">trans</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}})</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">static</span><span class="w"> </span><span class="p">(</span><span class="nb">disjoin</span><span class="w"> </span><span class="nb">procedure?</span><span class="w"> </span><span class="nb">set!-transformer?</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"syntax transformer"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">trans.value</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)])))</span></code></pre><p>This uses the handy <code>static</code> syntax class that comes with <code>syntax/parse</code>, which implicitly handles the call to <code>syntax-local-value</code> and produces a nice error message if the value returned does not match a predicate. All we have to do is apply the transformer value bound to the <code>trans.value</code> attribute using <code>local-apply-transformer</code>, and now the <code>expand-macro</code> can be written in just a couple lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">expand-inside</span> +<span class="w"> </span><span class="kd">#:track-literals</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:do-expand-inside</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form.expansion</span><span class="p">])</span></code></pre><p>(Using the <code>#:track-literals</code> option, also new in Racket v7.0, ensures that Check Syntax will be able to recognize the uses of <code>$expand</code> that disappear from after <code>expand-inside</code> is expanded.)</p><p>Putting everything together, our example from above really works:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>That’s it. All told, the entire implementation is only about 30 lines of code. For a full, compilable, working example, see <a href="https://gist.github.com/lexi-lambda/65d69043023b519694f50dfca2dc7d33">this gist</a>.</p><ol class="footnotes"></ol></article>Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitionshttps://lexi-lambda.github.io/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/https://lexi-lambda.github.io/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/13 Sep 2018<article><p>In my <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">previous blog post</a>, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.</p><h2><a name="what-are-internal-definitions"></a>What are internal definitions?</h2><p>This blog post is going to be largely focused on how to properly implement a form that handles the expansion of <em>internal definitions</em> in Racket. This is a tricky topic to get right, but before we can discuss internal definitions, we have to establish what definitions themselves are and how they relate to other binding forms.</p><p>In a traditional Lisp, there are two kinds of bindings: top-level bindings and local bindings. In Scheme and its descendants, this distinction is characterized by two different binding forms, <code>define</code> and <code>let</code>. To a first approximation, <code>define</code> is used for defining top-level, global bindings, and it resembles variable definitions in many mainstream languages in the sense that definitions using <code>define</code> are not really expressions. They don’t produce a value, they define a new binding. Definitions written with <code>define</code> look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="s2">"hello"</span><span class="p">)</span></code></pre><p>Each definition is made up of two parts: the <em>binding identifier</em>, in this case <code>x</code> and <code>y</code>, and the <em>right hand side</em>, or RHS for short. Each RHS is a single expression that will be evaluated and used as the value for the introduced binding.</p><p>In Scheme and Racket, <code>define</code> also supports a shorthand form for defining functions in a natural syntax without the explicit need to write <code>lambda</code>, which looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">double</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span></code></pre><p>However, this is just syntactic sugar. The above form is really just a macro for the following equivalent, expanded version:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">double</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)))</span></code></pre><p>Since we only care about fully-expanded programs, we’ll focus exclusively on the expanded version of <code>define</code> in this blog post, since if we handle that, we’ll also handle the function shorthand’s expansion.</p><p>In contrast to <code>define</code>, there is also <code>let</code>, which has a rather different shape. A <code>let</code> form <em>is</em> an expression, and it creates local bindings in a delimited scope:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>The binding clauses of a <code>let</code> expression are known as the <em>binding pairs</em>, and the sequence of expressions afterwards are known as the <em>body</em> of the <code>let</code>. Each binding pair consists of a binding identifier and a RHS, just like a top-level definition created with <code>define</code>, but while <code>define</code> is a standalone form, the binding pairs cannot meaningfully exist outside of a <code>let</code>—they are recognized as part of the grammar of the <code>let</code> form itself.</p><p>Like other Lisps, Racket distinguishes between top-level—or, more precisely, <em>module-level</em>—bindings and local bindings. A module-level binding can be exported using <code>provide</code>, which will allow other modules to access the binding by importing the module with <code>require</code>. Such definitions are treated specially by the macroexpander, compiler, and runtime system alike. There is a pervasive, meaningful difference between module-level definitions and local definitions besides simply scope.</p><p>I am making an effort to make this as clear as possible before discussing internal definitions because without it, the following point can be rather confusing: internal definitions are written using <code>define</code>, but they are local bindings, <em>not</em> module-level ones! In Racket, <code>define</code> is allowed to appear in the body of virtually all block forms like <code>let</code>, so the following is a legal program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>This program is equivalent to the one expressed using <code>let</code>. In fact, when the Racket macroexpander expands these local uses of <code>define</code>, it actually translates them into uses of <code>letrec</code>. After expanding the above expression, it would look closer to the following:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span></code></pre><p>In this sense, <code>define</code> is a form with a double life in Racket. When used at the module level, it creates module-level definitions, which remain in a fully-expanded program and can be imported by other modules. When used inside local blocks, it creates internal definitions, which do not remain in fully expanded programs, since they are translated into recursive local binding forms.</p><p>In this blog post, we will ignore module-level definitions. Like in the previous blog post, we will focus exclusively on expanding expressions, not whole modules. However, we will extend our language to allow internal definitions inside local binding forms, and we will translate them into <code>letrec</code> forms in the same way as the Racket macroexpander.</p><h2><a name="revisiting-and-generalizing-the-expression-expander"></a>Revisiting and generalizing the expression expander</h2><p>In the previous blog post, our expander expanded types, which were essentially expressions from the perspective of the Racket macroexpander. We wrote a syntax class that handled the parsing of a restricted type grammar that disallowed most Racket-level expression forms, like <code>begin</code>, <code>if</code>, <code>#%plain-lambda</code>, and <code>quote</code>. After all, Hackett is not dependently-typed, and it disallows explicit type abstraction to preserve type inference, so it would be a very bad thing if we allowed <code>if</code> or explicit lambda abstraction to appear in our types. For this blog post, however, we will restructure the type expander to handle the full grammar of expressions permitted by Racket.</p><p>While the syntax class approach used in the previous blog post was cute, this blog post will use ordinary functions defined at phase 1 instead of syntax classes. In practice, this provides superior error reporting, since it reports syntax errors in terms of the form that went wrong, not the form prior to expansion. Since we can still use <code>syntax-parse</code> to parse the arguments to these functions, we don’t lose any expressive power in the expression of our pattern language.</p><p>To start, we’ll extract the call to <code>local-expand</code> into its own function. This corresponds to the <code>type</code> syntax class from the previous blog post, but we’ll use phase 1 parameters to avoid threading so many explicit function arguments around:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-context</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-stop-list</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">))))</span></code></pre><p>Due to the way <code>local-expand</code> implicitly extends the stop list, as discussed in the previous blog post, we can initialize the stop list to a list containing just <code>define-values</code> and <code>define-syntaxes</code>, and the other forms we care about will be included automatically.</p><p>Next, we’ll use this function to implement a <code>expand-expression</code> function, which will emulate the way the expander expands a single expression, as the name implies. We’ll ignore any custom core forms for now, so we’ll just focus exclusively on the Racket core forms.</p><p>A few of Racket’s core forms are not actually subject to any expansion at all, and they expand to themselves. These forms are <code>quote</code>, <code>quote-syntax</code>, and <code>#%variable-reference</code>. Additionally, <code>#%top</code> is not something useful to handle ourselves, since it involves no recursive expansion, so we’ll treat it as if it expands to itself as well and allow the expander to raise any unbound identifier errors it produces. Here’s what the <code>expand-expression</code> function looks like when exclusively handling these things:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Another set of Racket core forms are simple expressions which contain subforms, all of which are themselves expressions. These forms include things like <code>#%expression</code>, <code>begin</code>, and <code>if</code>, and they can be expanded recursively. We’ll add another clause to handle these, which can be written with a straightforward recursive call to <code>expand-expression</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Another easy form to handle is <code>set!</code>, since it also requires simple recursive expansion, but it can’t be handled in the same way as the above forms since one of its subforms (the variable to mutate) should not be expanded. It needs another small clause:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:set!</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)))]</span></code></pre><p>The other expressions are harder, since they’re all the binding forms. Fully-expanded Racket code has four local binding forms: <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, and <code>letrec-values</code>. Additionally, as discussed in the previous blog post, <code>local-expand</code> can also produce <code>letrec-syntaxes+values</code> forms produced by local syntax bindings. In the type expander, we completely disallowed runtime bindings from appearing in the resulting program, so we completely removed <code>letrec-syntaxes+values</code> in our expansion, but in the case of handling arbitrary Racket programs, we actually want to leave a <code>letrec-values</code> form behind to hold any runtime bindings (i.e. the <code>values</code> part of <code>letrec-syntaxes+values</code>).</p><p>We’ll start with <code>#%plain-lambda</code>, which is the simplest of all the five aforementioned binding forms. It binds a sequence of identifiers at runtime, and they are in scope within the body of the lambda expression. Just as we created and used an internal-definition context to hold the bindings of a <code>letrec-syntax+values</code> form in the previous blog post, we’ll do the same for Racket’s other binding forms as well:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>However, the above handling of <code>#%plain-lambda</code> isn’t <em>quite</em> right, since the argument list can also include a “rest argument” binding in addition to a sequence of positional arguments. To accommodate this, we can introduce a simple syntax class that handles the different permutations:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">plain-formals</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"formals"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[[</span><span class="n">id</span><span class="w"> </span><span class="mi">1</span><span class="p">]]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id*:id</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id**:id</span><span class="p">)</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">id*</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">id**</span><span class="p">]]))</span></code></pre><p>Now we can use this to adjust <code>#%plain-lambda</code> to handle rest arguments:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Next, we’ll handle <code>case-lambda</code>. As it turns out, expanding <code>case-lambda</code> is almost exactly the same as expanding <code>#%plain-lambda</code>, except that it has multiple clauses. Since each clause is expanded identically to the body of a <code>#%plain-lambda</code>, and it even has the same shape, the clauses can be extracted into a separate syntax class to share code between the two forms:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]]))</span></code></pre><p>Now, both <code>#%plain-lambda</code> and <code>case-lambda</code> can be handled in a few lines of code each:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Finally, we need to tackle the three <code>let</code> forms. None of these involve any fundamentally new ideas, but they are a little bit more involved than the variants of lambda due to the need to handle the RHSs. Each variant is slightly different, but not dramatically so: the bindings aren’t in scope when expanding the RHSs of <code>let-values</code>, but they are for <code>letrec-values</code> and <code>letrec-syntaxes+values</code>, and <code>letrec-syntaxes+values</code> creates transformer bindings and must evaluate some RHSs in phase 1 while <code>let-values</code> and <code>letrec-values</code> exclusively bind runtime bindings. It would be possible to implement these three forms in separate clauses, but since we’d ideally like to duplicate as little code as possible, we can write a rather elaborate <code>syntax/parse</code> pattern to handle all three binding forms all at once.</p><p>We’ll start by handling <code>let-values</code> alone to keep things simple:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>This isn’t dramatically different from the implementation of <code>#%plain-lambda</code>. The only difference is that we have to recursively invoke <code>expand-expression</code> on the RHSs in addition to expanding the body expressions. To handle <code>letrec-values</code> in the same clause, however, we’ll have to get a little more creative.</p><p>So far, we haven’t actually tapped very far into <code>syntax/parse</code>’s pattern language over the course of these two blog posts. The full language available to patterns is rather extensive, and we can take advantage of that to write a modification of the above clause that handles both <code>let-values</code> and <code>letrec-values</code> at once:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}}}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>The <code>~bind</code> pattern allows us to explicitly control how attributes are bound as part of the pattern-matching process, which allows us to track when we want to enable the recursive binding behavior of <code>letrec-values</code> in our handler code. Since the vast majority of the logic is otherwise identical, this is a significant improvement over duplicating the clause.</p><p>Adding support for <code>letrec-syntaxes+values</code> is done in the same general way, but the pattern is even more involved. In addition to tracking whether or not the bindings are recursive, we have to track if any syntax bindings were present at all, and if they were, bind them with <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span></code></pre><p>This behemoth clause handles all three varieties of <code>let</code> forms that can appear in the result of <code>local-expand</code>. Notably, in the <code>letrec-syntaxes+values</code> case, we expand into <code>letrec-values</code>, since the transformer bindings are effectively erased, and we use <code>syntax-track-origin</code> to record that the result originally came from a use of <code>letrec-syntaxes+values</code>.</p><p>With these five clauses, we’ve handled all the special forms that can appear in expression position in Racket’s kernel language. To tie things off, we just need to handle the cases of a variable reference, which is represented by a bare identifier not bound to syntax, or literal data, like numbers or strings. We can add one more clause at the end to handle those:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span></code></pre><p>Putting them all together, our <code>expand-expression</code> function looks as follows:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> + +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">])))</span></code></pre><p>If we try it out, we’ll see that it really does work! Even complicated local binding forms are handled properly by our expander:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">))))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">(((</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>We are now able to expand arbitrary Racket expressions in the same way that the expander does. While this might not seem immediately useful—after all, we haven’t actually gained anything here over just calling <code>local-expand</code> with an empty stop list—we can use this as the basis of an expander that can extensibly handle custom core forms, which I may cover in a future blog post.</p><h2><a name="adding-support-for-internal-definitions"></a>Adding support for internal definitions</h2><p>In the previous section, we defined an expander that could expand arbitrary Racket expressions, but our expander is still imperfect: we still do not support internal definitions. For all forms that have bodies, including <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, and <code>letrec-syntaxes+values</code>, Racket permits the use of internal definitions.</p><p>In practice, internal-definition contexts allow for an increased degree of modularity compared to traditional local binding forms, since they provide an <em>extensible</em> binding language. Users may mix many different binding forms within a single definition context, such as <code>define</code>, <code>define-syntax</code>, <code>match-define</code>, and even <code>struct</code>. However, this means the rewriting process described earlier in this blog post is not as simple as detecting the definitions and lifting them into a local binding form, since it’s not immediately apparent which forms are binding forms and which are expressions!</p><p>For this reason, expanding internal-definition contexts happens to be a nontrivial problem in itself. It involves a little more care than expanding expressions does, since it requires using partial expansion to discover which forms are definitions and which forms are expressions. We must take care to never expand too much, but also to expand enough that we reveal all uses of <code>define-values</code> and <code>define-syntaxes</code> (which all definition forms eventually expand into). We also must handle the splicing behavior of <code>begin</code>, which is necessary to allow single forms to expand into multiple definitions.</p><p>We’ll start by writing an <code>expand-body</code> function, which operates similarly to our previous <code>expand-expression</code> function. Unlike <code>expand-expression</code>, <code>expand-body</code> will accept a <em>list</em> of syntax objects, which represents the sequence of forms that make up the body. Logically, each body will create a first-class definition context with <code>syntax-local-make-definition-context</code> to represent the sequence of definitions:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>The bulk of our <code>expand-body</code> function will be a loop that partially expands body forms, adds definitions to the definition context as it discovers them, and returns the expressions and runtime definitions to be rewritten into binding pairs for a <code>letrec-values</code> form. Additionally, the loop will also track so-called <em>disappeared uses</em> and <em>disappeared bindings</em>, which are attached to the expansion using syntax properties to allow tools like DrRacket to learn about the binding structure of phase 1 definitions that are erased as part of macroexpansion.</p><p>The skeleton of this loop is relatively straightforward to write. We will iterate over the syntax objects that make up the body, expand them, and process the expansion using <code>syntax-parse</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">)))))))</span></code></pre><p>The hard part, of course, is actually handling the potential results of that expansion. We need to handle three forms specially: <code>begin</code>, <code>define-values</code>, and <code>define-syntaxes</code>. All other results of partial expansion will be treated as expressions. We’ll start by handling <code>begin</code>, since it’s the simplest case; we only need to prepend the subforms to the list of body forms to be processed, then continue looping:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>However, as is often the case, this isn’t quite perfect, since the information that these forms came from a surrounding <code>begin</code> is lost, which tools like DrRacket want to know. To solve this problem, the expander adjusts the <code>origin</code> property for all spliced forms, which we can mimic using <code>syntax-track-origin</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This is sufficient for <code>begin</code>, so we can move onto the actual definitions themselves. This actually isn’t too hard, since we just need to add the bindings we discover to the first-class definition context and preserve <code>define-values</code> bindings as binding pairs:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This solution is missing one thing, however, which is the use of <code>syntax-local-identifier-as-binding</code> to any use-site scopes that were added to the binding identifier while expanding the binding form in the definition context. Explaining precisely why this is necessary is outside the scope of this blog post, and is best understood by reading <a href="http://www.cs.utah.edu/plt/scope-sets/pattern-macros.html#%28part._use-site%29">the section on use-site scopes</a> in the paper that describes the theory behind Racket’s current macro system, Bindings as Sets of Scopes. In any case, the impact on our implementation is small:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>Finally, as with <code>begin</code>, we want to track that the binding pairs we generate actually came from a use of <code>define-values</code> (which in turn likely came from a use of some other definition form). Therefore, we’ll add another use of <code>syntax-track-origin</code> to copy and extend the necessary properties:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>That’s it for <code>define-values</code>. All that’s left is to handle <code>define-syntaxes</code>, which is quite similar, but instead of storing the definition in a binding pair, its RHS is immediately evaluated and added to the definition context using <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span></code></pre><p>As the above snippet indicates, this is also where the disappeared uses and disappeared bindings come in. In previous cases, we’ve used <code>syntax-track-origin</code> to indicate that a piece of syntax was the result of expanding a different piece of syntax, but in this case, <code>define-syntaxes</code> doesn’t expand into anything at all; it’s simply removed from the expansion entirely. Therefore, we need to resort to tracking the information in syntax properties on the resulting <code>letrec-values</code> form, so we’ll save them for later.</p><p>Finally, to finish things up, we can add a catchall clause that handles all other forms, which are now guaranteed to be expressions:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This completes our loop that processes definition forms, so all that’s left to do is handle the results. The only significant remaining work is to actually expand the RHSs of the binding pairs we collected and the body expressions, which can be done by calling our own <code>expand-expression</code> function directly:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span></code></pre><p>Finally, we can assemble all the pieces together into a single local binding form with the appropriate syntax properties:</p><pre><code class="pygments"><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))</span></code></pre><p>That’s it. We’ve now written an <code>expand-body</code> function that can process internal definition contexts in the same way that the macroexpander does. Overall, the whole function is just under 45 lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)))))</span></code></pre><p>The next step is to actually use this function. We need to replace certain recursive calls to <code>expand-expression</code> with calls to <code>expand-body</code>, but if we do this naïvely, we’ll have some problems. Currently, when we expand body forms, they’re always immediately inside another definition context (i.e. the bindings introduced by lambda formals or by <code>let</code> binding pairs), but they haven’t actually been expanded in that context yet. When we call <code>expand-body</code>, we create a nested context, which will inherit the bindings, but won’t automatically add the parent context’s scope. Therefore, we need to manually call <code>internal-definition-context-introduce</code> on the body syntax objects before calling <code>expand-body</code>. We can write a small helper function to make this easier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="n">stxs</span><span class="w"> </span><span class="n">ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">ctx</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stxs</span><span class="p">))))))</span></code></pre><p>Now we just need to replace the relevant calls to <code>expand-expression</code> with calls to <code>expand-body/in-ctx</code>, starting with a minor adjustment to our <code>lambda-clause</code> syntax class from earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="p">]]))</span></code></pre><p>The only other change must occur in the handling of the various <code>let</code> forms, which similarly replaces <code>expand-expression</code> with <code>expand-body/in-ctx</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">)))]</span></code></pre><p>With these changes, we’ve now extended our expression expander with the ability to expand internal definitions. We can see this in action on a simple example:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>Just as we’d like, the transformer bindings were expanded and subsequently eliminated, and the runtime binding was collected into a <code>letrec-values</code> form. The outer <code>let-values</code> is left over from the outer <code>let</code>, which is needed only to create an internal-definition context to hold our internal definitions.</p><h2><a name="putting-the-expression-expander-to-work"></a>Putting the expression expander to work</h2><p>So far, we’ve done a lot of work to emulate the behavior of Racket’s macroexpander, and as the above example demonstrates, we’ve been fairly successful in that goal. However, you might be wondering <em>why</em> we did any of this, as replicating the behavior of <code>local-expand</code> is not very useful on its own. As mentioned above, this can be used as the foundation of an expander for custom core forms that extends, rather than replaces, the built-in Racket core forms, It can also be used to “cheat” and expand through the behavior of the <code>local-expand</code> stop list, which implicitly adds the Racket core forms to any non-empty stop list. Hopefully, I’ll have a chance to cover some of these things more deeply in the future, but for now, I’ll just give a small taste of the latter.</p><p>By using the power of our <code>expand-expression</code> function, it’s actually possible to use this kind of expression expander to do genuinely nefarious things, such as hijack the behavior of arbitrary macros! For example, we could do something evil like make <code>for</code> loops run in reverse order by adding <code>for</code> to <code>current-stop-list</code>, then adding an additional special case to <code>expand-expression</code> for <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">for</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="k">for</span><span class="p">]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:for</span><span class="w"> </span><span class="p">([</span><span class="n">x:id</span><span class="w"> </span><span class="n">seq:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="p">(</span><span class="nb">sequence-&gt;list</span><span class="w"> </span><span class="n">seq</span><span class="p">)))]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>Amazingly, due to the fact that we’ve taken complete control of the expansion process, this will rewrite uses of <code>for</code> <em>even if they are introduced by macroexpansion</em>. For example, we could write a small macro that expands into a use of <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="nb">in-range</span><span class="w"> </span><span class="n">n</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">i</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span> +<span class="mi">0</span> +<span class="mi">1</span> +<span class="mi">2</span> +<span class="mi">3</span> +<span class="mi">4</span></code></pre><p>If we write a wrapper macro that applies our evil version of <code>expand-expression</code> to its body, then wrap a use of our <code>print-up-to</code> macro with it, it will execute the loop in reverse order:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:expr</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span><span class="p">)])</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>On its own, this is not that impressive, since we could have just used <code>local-expand</code> on the body directly to achieve this. However, what’s remarkable about <code>hijack-for-loops</code> is that it will work even if the <code>for</code> loop is buried deep inside some arbitrary expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">foo</span> +<span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">))))</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="mi">5</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>Of course, this example is rather contrived—mucking with <code>for</code> loops like this isn’t useful at all, and nobody would really write <code>print-up-to</code> as a macro, anyway—but there is potential for using this technique to do more interesting things.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p>The system outlined in this blog post is not something I would recommend using in any real macro. It is enormously complicated, requires knowledge well above that of your average working macrologist, and it involves doing rather horrible things to the macro system, things it was undoubtably never designed to do. Still, I believe this blog post is useful, for a few different reasons:</p><ol><li><p>The technology outlined in this post, while perhaps not directly applicable to existing real-world problems, provides a framework for implementing various new kinds of syntax transformations in Racket <em>without</em> extending the macro system. It demonstrates the expressive power of the macro system, and it hopefully lays the foundation for a better, more high-level interface for users who wish to define their own languages with custom core forms.</p></li><li><p>This system provides insight into the way the Racket macroexpander operates, <em>in terms of the userspace syntax API</em>. The canonical existing model of hygienic macroexpansion, in the aforementioned <a href="http://www.cs.utah.edu/plt/scope-sets/">Bindings as Sets of Scopes</a> paper, does not explain the workings of internal definition contexts in detail, and it certainly doesn’t explain them in terms that a Racket programmer would already be familiar with. By reencoding those ideas within the macro system itself, an advanced macro writer may be able to more easily connect concepts in the macro system’s implementation to concepts they have already been exposed to.</p></li><li><p>The capability of the proof-of-concept outlined here demonstrates that the limitation imposed by the existing implementation of the stop list (namely, the way it is implicitly extended with additional identifiers) is essentially artificial, and it can be hacked around with sufficient (albeit significant) effort. This isn’t enormously important, but it is somewhat relevant to a recent debate in <a href="https://github.com/racket/racket/issues/2154">a GitHub issue</a> about the handling of the <code>local-expand</code> stop list.</p></li><li><p>Finally, for myself as much as anyone else, this implementation records in a concise way (perhaps overly concise at times) the collection of very subtle details I’ve learned over the past six months about how information is preserved and propagated during the expansion process.</p></li></ol><p>This blog post is not for everybody. If you made it to the end, give yourself a pat on the back. If you made it to the end <em>and</em> understood everything you read: congratulations, you are a certified expert in Racket macro programming. If not, do not fear, and do not lose hope—I plan for something significantly more mellow next time.</p><p>As always, I’d like to give thanks to the people who contributed significantly, if indirectly, to the contents of this blog post, namely <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://mballantyne.net">Michael Ballantyne</a>, and <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a>. And finally, for those interested, all of the code in this blog post can be found in a runnable form <a href="https://gist.github.com/lexi-lambda/c4f4b91ac9c0a555447d72d02e18be7b">in this GitHub gist</a>.</p><ol class="footnotes"></ol></article>Reimplementing Hackett’s type language: expanding to custom core forms in Rackethttps://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/https://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/15 Apr 2018<article><p>In the past couple of weeks, I <a href="https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935">completely rewrote the implementation of Hackett’s type language</a> to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.</p><p>This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">my previous blog post on Hackett</a>, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.</p><h2><a name="what-are-core-forms"></a>What are core forms?</h2><p>Before we can get started writing <em>custom core forms</em>, we need to understand the meaning of Racket’s plain old <em>core forms</em>. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.</p><p>To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a <code>#lang</code> line at the top. In this case, we’ll use <code>#lang racket</code> to keep things simple:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as <em>reading</em> the program. The <code>#lang</code> line controls how the program is read—some <code>#lang</code>s provide parsers that allow syntax that is very different from the parser used for <code>#lang racket</code>—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span></code></pre><p>Note the introduction of <code>#%module-begin</code>. Despite the fancy name, this is really just an ordinary macro provided by the <code>racket</code> language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with <code>#%module-begin</code>; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.</p><p>One the program has been read, it is subsequently <em>expanded</em> by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">2</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">call-with-values</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="n">add2</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">))</span> +<span class="w"> </span><span class="n">print-values</span><span class="p">)))</span></code></pre><p>Let’s note the things that changed:</p><ol><li><p><code>#%module-begin</code> was replaced with <code>#%plain-module-begin</code>. <code>#%plain-module-begin</code> is a binding that wraps the body of every expanded module, and all definitions of <code>#%module-begin</code> in any language must eventually expand to <code>#%plain-module-begin</code>. However, <code>#lang racket</code>’s <code>#%module-begin</code> doesn’t <em>just</em> expand to <code>#%plain-module-begin</code>, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints <code>5</code> even though there is no code related to printing in the original program!</p></li><li><p>The lambda shorthand used with <code>define</code> was converted to an explicit use of <code>lambda</code>, and it was expanded to <code>define-values</code>. In Racket, <code>define</code> and <code>define-syntax</code> are really just macros for <code>define-values</code> and <code>define-syntaxes</code> that only bind a single identifier.</p></li><li><p>All function applications were tagged explicitly with <code>#%plain-app</code>. This syntactically distinguishes function applications from uses of forms like <code>define-values</code> or <code>lambda</code>. It also allows languages to customize function application by providing their own macros named <code>#%app</code> (just like languages can provide their own macros named <code>#%module-begin</code> that expand to <code>#%plain-module-begin</code>), but that is outside the scope of this blog post.</p></li><li><p>All literals have been wrapped with <code>quote</code>, so <code>2</code> became <code>'2</code> and <code>3</code> became <code>'3</code>.</p></li></ol><p>Importantly, the resulting program contains <strong>no macros</strong>. Such programs are called <em>fully expanded</em>, since all macros have been eliminated and no further expansion can take place.</p><p>So what’s left behind? Well, some of the things in the program are literal data, like the numbers <code>2</code> and <code>3</code>. There are also some variable references, <code>x</code> and <code>add2</code>. Most of the program, however, is built out of primitives like <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, and <code>lambda</code>. These primitives are <em>core forms</em>—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.</p><p>In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:</p><pre><code class="pygments"><span class="n">var</span> <span class="n">add2</span> <span class="o">=</span> + <span class="n">function</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span> + +<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">add2</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span></code></pre><p>Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be <em>compiled</em>. The Racket compiler has built-in rules for how to compile core forms like <code>define-values</code>, <code>lambda</code>, and <code>#%plain-app</code>, and the result is optimized Racket bytecode.</p><p>In the remainder of this blog post, as most discussions of macros do, we’ll ignore the <em>read</em> and <em>compile</em> steps of the Racket program pipeline and focus exclusively on the <em>expand</em> step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.</p><h3><a name="racket-s-default-core-forms"></a>Racket’s default core forms</h3><p>So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, <code>lambda</code>, and <code>quote</code>, but there are many more. The full list is available in the section of the Racket reference named <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29">Fully Expanded Programs</a>, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like <code>define-syntaxes</code>, <code>if</code>, <code>let-values</code>, <code>letrec-values</code>, <code>begin</code>, <code>quote-syntax</code>, and <code>set!</code>. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.</p><p>These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, <code>#lang racket</code> implements <code>cond</code> as a macro that eventually expands into <code>if</code>, and it implements <code>syntax</code> as a macro that eventually expands into function calls and <code>quote-syntax</code>. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s <code>match</code> can expand into uses of <code>let</code> and <code>cond</code>, and it doesn’t need to concern itself with using <code>let-values</code> and <code>if</code>. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.</p><h3><a name="the-need-for-custom-core-forms"></a>The need for custom core forms</h3><p>With that in mind, why might we wish to define <em>custom</em> core forms? In fact, what would such a thing even mean? By their very nature, <em>all</em> Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.</p><p>Despite this, there <em>are</em> at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.</p><h4><a name="supporting-multiple-backends"></a>Supporting multiple backends</h4><p>The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket <code>#lang</code> that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might <em>also</em> wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.</p><p>In the case of a hardware description language, the custom core forms might include things like <code>input</code> and <code>output</code> for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could <em>additionally</em> define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.</p><p>Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.</p><h4><a name="compiling-an-extensible-embedded-language"></a>Compiling an extensible embedded language</h4><p>A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.</p><p>This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it <em>will</em> eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.</p><p>Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the <code>match</code> macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.</p><p>Existing DSLs of this type are rare, but they do exist. <code>syntax/parse</code> provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from <code>racket/match</code> to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While <code>match</code> is essentially just a traditional pattern-matcher, albeit an extensible one, <code>syntax-parse</code> is its own programming language, closer in some ways to Prolog than to Racket.</p><p>For this reason, <code>syntax/parse</code> has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as <a href="http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29"><em>pattern directives</em></a>. Here is an example of pattern directives in action, from my own <code>threading</code> library:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>Each directive is represented by a keyword, in this case <code>#:do</code> and <code>#:with</code>. Each directive has a corresponding keyword in the pattern language, in this case <code>~do</code> and <code>~parse</code>. Therefore, the above pattern could equivalently be written this way:</p><pre><code class="pygments"><span class="p">[{</span><span class="n">~and</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">~do</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using <code>#:declare</code>, so this is also equivalent:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">expr</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a <code>#:with</code> or <code>~parse</code> pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using <code>#:do</code> or <code>~do</code>, practical uses of <code>syntax/parse</code> really do involve quite a lot of programming in its pattern DSL.</p><p>But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—<code>#:declare</code>, <code>#:do</code>, and <code>#:with</code>, among others—are essentially the core forms of <code>syntax/parse</code>’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.</p><p>But what if <code>syntax/parse</code> could define its own core forms? What if, instead of <code>#:do</code>, <code>#:declare</code>, and <code>#:with</code> being implemented as keyword options specially recognized by the <code>syntax-parse</code> grammar, it defined <code>do</code>, <code>declare</code>, and <code>with</code> as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the <code>syntax/parse</code> core forms. The implementation of <code>syntax/parse</code> could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.</p><p>Now, to be fair, <code>syntax/parse</code> is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.</p><p>The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.</p><h2><a name="the-need-for-a-custom-type-language-in-hackett"></a>The need for a custom type language in Hackett</h2><p>In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for <em>both</em> of them:</p><ul><li><p>Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.</p></li><li><p>Hackett can <em>also</em> make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.</p></li></ul><p>The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:</p><ul><li><p><code>(#%type:con <em>id</em>)</code> — Type constructors, like <code>Integer</code> or <code>Maybe</code>. These are one of the fundamental building blocks of Hackett types.</p></li><li><p><code>(#%type:app <em>type</em> <em>type</em>)</code> — Type application, such as <code>(Maybe Integer)</code>. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of <code>#%type:app</code>.</p></li><li><p><code>(#%type:forall <em>id</em> <em>type</em>)</code> — Universal quantification. This is essentially a binding form, which binds any uses of <code>(#%type:bound-var <em>id</em>)</code> in <code><em>type</em></code>.</p></li><li><p><code>(#%type:qual <em>type</em> <em>type</em>)</code> — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like <code>Eq</code> are bound as type constructors.</p></li><li><p>Finally, Hackett types support three different varieties of type variables:</p><ul><li><p><code>(#%type:bound-var <em>id</em>)</code> — Bound type variables. These are only legal under a corresponding <code>#%type:forall</code>.</p></li><li><p><code>(#%type:wobbly-var <em>id</em>)</code> — Solver variables, which may unify with any other type as part of the typechecking process.</p></li><li><p><code>(#%type:rigid-var <em>id</em>)</code> — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.</p></li></ul></li></ul><p>To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we <em>can</em> bind them to macros that do nothing but raise an exception if something attempts to expand them:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span></code></pre><p>This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.</p><h3><a name="expanding-types-in-our-type-language"></a>Expanding types in our type language</h3><p>We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we <em>do</em> want to expand some of their subforms. For example, in the type <code>(#%type:app a b)</code>, we want to recursively expand <code>a</code> and <code>b</code>.</p><p>In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a>, and it has an option relevant to our needs: the stop list.</p><p>Often, <code>local-expand</code> is used to force the expander to completely, recursively expand a form. For example, by using <code>local-expand</code>, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="c1">; =&gt; (let-values ([(x) &#39;1]) (#%plain-app + x &#39;2))</span></code></pre><p>The third argument to <code>local-expand</code> is the <em>stop list</em>, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; =&gt; (#%type:forall x t)</span></code></pre><p>Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the <code>#%type:forall</code> identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding <code>t</code> since the expander has no way of knowing which pieces of <code>(#%type:forall x t)</code> it should expand! In this case, we want it to recur to expand <code>t</code>, since it should be a type, but not <code>x</code>, since <code>#%type:forall</code> essentially puts <code>x</code> in binding position.</p><p>Therefore, we have to get more clever. We need to call <code>local-expand</code> to produce a type, then we have to pattern-match on it and subsequently call <code>local-expand</code> <em>again</em> on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.</p><p>One good way to do this is to use <code>syntax/parse</code> syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using <code>local-expand</code> and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled <code>#%type:con</code> and <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:expanded-type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]))</span></code></pre><p>This blog post is definitely <em>not</em> a <code>syntax/parse</code> tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named <code>expansion</code>. This attribute contains the fully expanded version of the type currently being parsed. In the <code>#%type:con</code> case, <code>expansion</code> is just <code>this-syntax</code>, which holds the current piece of syntax being parsed. This makes sense, since uses of <code>#%type:con</code> just expand to themselves—expanding <code>(#%type:con Maybe)</code> should not perform any additional expansion on <code>Maybe</code>. This is one of Hackett’s atomic types.</p><p>In contrast, <code>#%type:app</code> <em>does</em> recursively expand its arguments. By annotating its two subforms with <code>:type</code>, the <code>type</code> syntax class will invoke <code>local-expand</code> on each subform, which will in turn use <code>expanded-type</code> to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once <code>a</code> and <code>b</code> have been expanded, <code>#%type:app</code> reassembles them into a new syntax object using <code>#'(#%type:app a.expansion b.expansion)</code>, which replaces their unexpanded versions with their new, expanded versions.</p><p>We can see this behavior by writing a small <code>expand-type</code> function that will expand its argument:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>Now we can use it to observe what happens when we try expanding a type using <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; #%type:app: expected type</span> +<span class="c1">; at: Maybe</span> +<span class="c1">; in: (#%type:app Maybe Integer)</span></code></pre><p>Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined <code>Maybe</code> or <code>Integer</code> anywhere. Let’s do so! We can define them as simple macros that expand into uses of <code>#%type:con</code>, which can be done easily using <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29"><code>make-variable-like-transformer</code></a> from <code>syntax/transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Maybe</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Integer</span><span class="p">)))</span></code></pre><p>Now, if we try expanding that same type again:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Integer))</span></code></pre><p>…it works! Neat. Now we just need to add the cases for the remaining forms in our type language:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">t:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like <code>let-syntax</code> to work in types. For example, we ought to be able to create a local type binding with <code>let-syntax</code> and have it just work. Unfortunately, it doesn’t:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; let-syntax: expected one of these identifiers: `#%type:con&#39;, `#%type:app&#39;, `#%type:forall&#39;, `#%type:qual&#39;, `#%type:bound-var&#39;, `#%type:wobbly-var&#39;, or `#%type:rigid-var&#39;</span> +<span class="c1">; at: letrec-syntaxes+values</span> +<span class="c1">; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))</span></code></pre><p>What went wrong? And why is it complaining about <code>letrec-syntaxes+values</code>? Well, if you read the documentation for <code>local-expand</code>, you’ll find that its behavior is a little more complicated than you might at first believe:</p><blockquote><p>If <em><code>stop-ids</code></em> is [a nonempty list containing more than just <code>module*</code>], then <code>begin</code>, <code>quote</code>, <code>set!</code>, <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, <code>if</code>, <code>begin0</code>, <code>with-continuation-mark</code>, <code>letrec-syntaxes+values</code>, <code>#%plain-app</code>, <code>#%expression</code>, <code>#%top</code>, and <code>#%variable-reference</code> are implicitly added to <em><code>stop-ids</code></em>. Expansion stops when the expander encounters any of the forms in <em><code>stop-ids</code></em>, and the result is the partially-expanded form.</p></blockquote><p>That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with <code>letrec-syntaxes+values</code> (which <code>let-syntax</code> expands to) is a reasonable one. If the expander naïvely expanded <code>letrec-syntaxes+values</code> in the presence of a nonempty stop list, it could cause some significant problems!</p><p>Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">))</span></code></pre><p>We see <code>let-syntax</code>, so we start by evaluating the expression on the right hand side of the <code>Bool</code> binding. This produces a transformer expression, so we bind <code>Bool</code> to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:</p><pre><code class="pygments"><span class="c1">; local bindings:</span> +<span class="c1">; Bool -&gt; #&lt;variable-like-transformer&gt;</span> +<span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)</span></code></pre><p>Now, the identifier in application position is <code>#%type:app</code>, and <code>#%type:app</code> is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the <code>let-syntax</code> needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is <code>(#%type:app Maybe Bool)</code>. But this is a problem, because when we then go to expand <code>Bool</code>, <code>Bool</code> isn’t in the local binding table anymore! The <code>let-syntax</code> was already erased, and <code>Bool</code> is unbound!</p><p>When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.</p><p>Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call <code>local-expand</code>. The bad news is that first-class definition contexts are <em>complicated</em>, and using them properly is a surprisingly subtle problem.</p><p>Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our <code>type</code> and <code>expanded-type</code> syntax classes so that we may thread a definition context around as we recursively expand:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now, we can add an additional case to <code>expanded-type</code> to handle <code>letrec-syntaxes+values</code>, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>But even this isn’t quite right. The problem with this implementation is that it throws away the existing <code>intdef-ctx</code> argument to <code>expanded-type</code>, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a <em>child</em> of the previous definition context by passing the old context as an argument to <code>syntax-local-make-definition-context</code>. This will ensure the parent bindings are brought into scope when expanding using the child context:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>With this in place, our example using <code>let-syntax</code> actually works!</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Bool))</span></code></pre><p>Pretty cool, isn’t it?</p><h3><a name="preserving-syntax-properties-and-source-locations"></a>Preserving syntax properties and source locations</h3><p>We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.</p><p>To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports <code>#%type:app</code> under a different name, like <code>#%type:apply</code>, we should expand to a piece of syntax that still has <code>#%type:apply</code> in application position instead of replacing it with <code>#%type:app</code>.</p><p>To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the <code>#%type:app</code> clause to the following:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span></code></pre><p>But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy <em>everything</em>, we can define two helper macros, <code>syntax/loc/props</code> and <code>quasisyntax/loc/props</code>, which are like <code>syntax/loc</code> and <code>quasisyntax/loc</code> but copy properties in addition to source location information:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span></code></pre><p>Using <code>syntax/loc/props</code>, we can be truly thorough about ensuring all properties are preserved:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span></code></pre><p>Applying this to the other relevant clauses, we get an updated version of the <code>expanded-type</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now we’re getting closer, but if you can believe it, even <em>this</em> isn’t good enough. The real expander’s implementation of <code>letrec-syntaxes+values</code> does two things our implementation does not: it copies properties and updates the <code>'origin</code> property to indicate the syntax came from a use of <code>letrec-syntaxes+values</code>, and it adds a <code>'disappeared-use</code> property to record the erased bindings for use by tools like DrRacket. We can apply <code>syntax-track-origin</code> and <code>internal-definition-context-track</code> to the resulting syntax to add the same properties the expander would:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span></code></pre><p>Now we’ve <em>finally</em> dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.</p><h3><a name="connecting-our-custom-language-to-hackett"></a>Connecting our custom language to Hackett</h3><p>It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-meta</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/parse</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/intdef</span> +<span class="w"> </span><span class="n">threading</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a <em>typechecker</em>. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.</p><p>But we don’t <em>just</em> need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom <code>#%app</code> binding that converts n-ary type applications to nested uses of <code>#%type:app</code>, as well as a nicer <code>forall</code> macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of <code>#%app</code>—one for types and one for values—since <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">Hackett supports multiple namespaces</a>, and each can have its own <code>#%app</code> binding.</p><p>The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom <code>syntax/parse</code> pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, <a href="https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt">it’s available on GitHub here</a>.</p><h2><a name="evaluation-limitations-and-acknowledgements"></a>Evaluation, limitations, and acknowledgements</h2><p>Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started <a href="https://twitter.com/lexi_lambda/status/976533916596097024">my new job</a> this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net <em>removal</em> of 100 lines from the rest of the codebase, which I consider an organizational win.</p><p>As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I <a href="https://twitter.com/lexi_lambda/status/985051504867446786">able to get it working</a> quickly and easily, I was able to do it in <a href="https://twitter.com/lexi_lambda/status/985052476473856000">no more than 20 lines of code</a>. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.</p><p>Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the <code>local-expand</code> stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes <code>syntax-parameterize</code>, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like <code>syntax-parameterize</code> cooperate better with things like Hackett’s type language.</p><p>Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> for <a href="https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf">his incredible work on constructing tools for the working macro developer</a>, including writing the fantastic <code>syntax/parse</code> library that powers essentially everything I do. Thank you both.</p><ol class="footnotes"></ol></article>User-programmable infix operators in Rackethttps://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/https://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/12 Aug 2017<article><p>Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in <a href="https://github.com/lexi-lambda/hackett">Hackett</a>, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.</p><p>Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done <em>without</em> modifying the stock <code>#lang racket</code> reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.</p><h2><a name="our-mission"></a>Our mission</h2><p>Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we <em>do</em> want:</p><ul><li><p>Infix operators should be user-extensible, not limited to a special set of built-in operators.</p></li><li><p>Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.</p></li><li><p>We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like <code>cons</code>. This allows <code>5 - 1 - 2</code> to be parsed as <code>(- (- 5 1) 2)</code>, but <code>5 :: 1 :: nil</code> to be parsed as <code>(:: 5 (:: 1 nil))</code>.</p></li></ul><p>These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:</p><ul><li><p>We will <strong>not</strong> permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing <code>(+ 1 2)</code> with <code>{1 + 2}</code>.</p></li><li><p>Our implementation will <strong>not</strong> support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.</p></li><li><p>All operators will be binary, and we will <strong>not</strong> support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.</p></li></ul><p>With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"infix.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> + +<span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">{</span><span class="mi">10</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="c1">; =&gt; &#39;(1 7)</span></code></pre><p>Let’s get started.</p><h2><a name="implementing-infix-operators"></a>Implementing infix operators</h2><p>Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:</p><ol><li><p>How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?</p></li><li><p>How can we associate fixity information with certain operators?</p></li></ol><p>We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.</p><p>So, how <em>do</em> we detect if a Racket expression is surrounded by curly braces? Normally, in <code>#lang racket</code>, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated <em>exactly</em> the same as parentheses:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.</p><p>Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29"><em>syntax objects</em></a>. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as <a href="http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29"><em>syntax properties</em></a>. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s <a href="http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29"><em>reader</em></a> (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named <a href="http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29"><code>'paren-shape</code></a>.</p><p>This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="no">#f</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\[</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\{</span></code></pre><p>This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.</p><h3><a name="customizing-application"></a>Customizing application</h3><p>Racket is a language <em>designed</em> to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named <a href="http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29"><code>#%app</code></a>, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>…into this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>What’s special about <code>#%app</code> is that the macroexpander will use whichever <code>#%app</code> is in scope in the expression’s lexical context, so if we write our own version of <code>#%app</code>, it will be used instead of the one from <code>#lang racket</code>. This is what we will use to hook into ordinary Racket expressions.</p><p>To write our custom version of <code>#%app</code>, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the <code>'paren-shape</code> syntax property, <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29"><code>syntax/parse/class/paren-shape</code></a>. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>This code will transform any applications surrounded in curly braces into one that starts with <code>#%infix</code> instead of <code>#%app</code>, so <code>{1 + 2}</code> will become <code>(#%infix 1 + 2)</code>, for example. The identifier <code>#%infix</code> isn’t actually special in any way, it just has a funny name, but we haven’t actually defined <code>#%infix</code> yet, so we need to do that next!</p><p>To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like <code>{1 + 2}</code>, should be converted into the equivalent prefix expressions, in this case <code>(+ 1 2)</code>. We can do this with a simple macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)])</span></code></pre><p>Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the <code>#%app</code> from <code>racket/base</code>, which will avoid any accidental infinite recursion between our <code>#%app</code> and <code>#%infix</code>. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>That’s pretty cool!</p><p>Of course, we probably want to support infix applications with more than just a single binary operator, such as <code>{1 + 2 + 3}</code>. We can implement that just by adding another case to <code>#%infix</code> that handles more subforms:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">10</span></code></pre><p>I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">5</span></code></pre><p>Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as <code>cons</code>, nested <code>-&gt;</code> types or contracts for curried functions, and <code>expt</code>, the exponentiation operator.</p><h3><a name="tracking-operator-fixity"></a>Tracking operator fixity</h3><p>Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.</p><p>Essentially, Racket (like Scheme) uses a <code>define-syntax</code> form to define macros, which is what <code>define-syntax-parser</code> eventually expands into. However, unlike Scheme, Racket’s <code>define-syntax</code> is not <em>just</em> for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Once a binding has been defined using <code>define-syntax</code>, a macro can look up the value associated with it by using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, which returns the compile-time value associated with an identifier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">foo</span><span class="p">)))</span> +<span class="c1">; =&gt; 3</span></code></pre><p>The cool thing is that <code>syntax-local-value</code> gets the value associated with a specific <em>binding</em>, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use <code>syntax-local-value</code> to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the <code>foo</code> binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because <code>foo</code> is not bound to anything at runtime.</p><p>To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the <a href="http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29"><code>prop:procedure</code></a> structure type property.</p><p>If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">))))</span></code></pre><p>This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for <code>runtime-binding</code>, but we can also include a value of our choosing for <code>fixity</code>. Here’s an example:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="nb">cons</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="p">))</span></code></pre><p>This new <code>::</code> binding will act, in every way, just like <code>cons</code>. If we use it in the REPL, you can see that it acts exactly the same:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></code></pre><p>However, we can also use <code>syntax-local-value</code> to extract this binding’s fixity at compile-time, and that’s what makes it interesting:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">::</span><span class="p">))))</span> +<span class="c1">; =&gt; &#39;right</span></code></pre><p>Using this extra compile-time information, we can adjust our <code>#%infix</code> macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used <code>syntax/parse/class/paren-shape</code> to make decisions based on the <code>'paren-shape</code> syntax property, we can use <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29"><code>syntax/parse/class/local-value</code></a> to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span></code></pre><p>Now, we can update <code>#%infix</code> to use our new <code>infix-op</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span></code></pre><p>Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via <code>#:when</code> clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of <code>#%infix</code>, we can successfully use <code>::</code> in an infix expression, and it will be parsed with the associativity that we expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Exciting!</p><h3><a name="a-nicer-interface-for-defining-infix-operators"></a>A nicer interface for defining infix operators</h3><p>We currently have to define infix operators by explicitly using <code>define-syntax</code>, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the <code>define-infix-operator</code> form from the example at the very beginning of this blog post.</p><p>Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the <code>define-syntax</code> definitions we were already writing:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span></code></pre><p>With this in hand, we can define some infix operators with a much nicer syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary <code>#lang racket</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">-1</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">256</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">64</span></code></pre><p>And you know what’s most amazing about this? The entire thing is <strong>only 50 lines of code</strong>. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/local-value</span> +<span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span> +<span class="w"> </span><span class="n">syntax/transformer</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>Racket is a hell of a programming language.</p><h2><a name="applications-limitations-and-implications"></a>Applications, limitations, and implications</h2><p>This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am <em>almost certain</em> that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even <em>locally</em>.</p><p>What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as <code>let-syntax</code> and <code>syntax-parameterize</code>. Using these tools, it would be entirely possible to implement a <code>with-fixity</code> macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make <code>/</code> right associative within a block of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="m">1/6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="nb">/</span><span class="w"> </span><span class="n">right</span><span class="p">])</span> +<span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span> +<span class="mi">1</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of <code>splicing-let</code> and <code>splicing-let-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span> +<span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="n">op:id</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}}]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">op-tmp</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">op</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let</span><span class="w"> </span><span class="p">([</span><span class="n">op-tmp</span><span class="w"> </span><span class="n">op</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">op-tmp</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span></code></pre><p>This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations <em>between</em> operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.</p><p>As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in <a href="https://github.com/lexi-lambda/hackett">Hackett</a> to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow <em>any</em> expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.</p><p>If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt">this file for the definition of infix bindings</a>, as well as <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101">this file for the defintion of infix application</a>. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like <code>{_ - 1}</code> to serve as a shorthand for <code>(lambda [x] {x - 1})</code>, but I haven’t yet decided if I like the tradeoffs involved.</p><p>It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in <code>#lang racket</code> without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.</p><p>Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to <code>#lang hackett</code>, there’s no reason something similar but more powerful couldn’t be built as a separate library by a <em>user</em> of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.</p><ol class="footnotes"></ol></article>Simple, safe multimethods in Rackethttps://lexi-lambda.github.io/blog/2016/02/18/simple-safe-multimethods-in-racket/https://lexi-lambda.github.io/blog/2016/02/18/simple-safe-multimethods-in-racket/18 Feb 2016<article><p>Racket ships with <code>racket/generic</code>, a system for defining <em>generic methods</em>, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports <em>single dispatch</em>. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.</p><h2><a name="motivating-multiple-dispatch"></a>Motivating multiple dispatch</h2><p>What is multiple dispatch and why is it necessary? Well, in most cases, it <em>isn’t</em> necessary at all. <a href="http://dl.acm.org/citation.cfm?doid=1449764.1449808">It has been shown that multiple dispatch is much rarer than single dispatch in practice.</a> However, when actually needed, having multiple dispatch in the toolbox is a valuable asset.</p><p>A classic example of multiple dispatch is multiplication over both scalars and vectors. Ideally, all of the following operations should work:</p><pre><code>2 × 3 = 6 +2 × ⟨3, 4⟩ = ⟨6, 8⟩ +⟨3, 4⟩ × 2 = ⟨6, 8⟩ +</code></pre><p>In practice, most languages do not support such flexible dispatch rules without fairly complicated branching constructs to handle each permutation of input types. Furthermore, since most languages only support single dispatch (such as most object-oriented languages), it is nearly impossible to add support for a new combination of types to an existing method.</p><p>To illustrate the above, even if a language supported operator overloading <em>and</em> it included a <code>Vector</code> class that overloaded multiplication to properly work with numbers and vectors, it might not implement matrix multiplication. If a user defines a <code>Matrix</code> class, they may overload <em>its</em> multiplication to support numbers, vectors, and matrices, but it is impossible to extend the multiplication implementation for the <code>Vector</code> class. That method is now completely set in stone, unless it is edited directly (and the programmer may not have access to <code>Vector</code>’s implementation).</p><p>Multiple dispatch solves all of these problems. Rather than specify implementations of functions for singular types, it is possible to specify implementations for sets of types. In the above example, a programmer would be able to define a new function that operates on <code>Vector</code> and <code>Matrix</code> arguments. Since each definition does not “belong” to any given type, extending this set of operations is trivial.</p><h2><a name="multiple-dispatch-in-racket"></a>Multiple dispatch in Racket</h2><p>This blog post is somewhat long and technical, so before proceeding any further, I want to show some real code that actually works so you can get a feel for what I’m talking about. As a proof-of-concept, I have created <a href="https://github.com/lexi-lambda/racket-multimethod">a very simple implementation of multiple dispatch in Racket</a>. The above example would look like this in Racket using my module:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Pardon the somewhat clunky syntax, but the functionality is there. Using the above code works as expected:</p><pre><code>&gt; (mul (num 2) (num 3)) +(num 6) +&gt; (mul (num 2) (vec '(3 4))) +(vec '(6 8)) +&gt; (mul (vec '(3 4)) (num 2)) +(vec '(6 8)) +</code></pre><p>Making the above snippet work is not particularly hard. In fact, it’s likely that most competent Racketeers could do it without much thought. However, there’s a tiny bit more going on behind the scenes than it may seem.</p><h2><a name="the-problem-with-multiple-dispatch"></a>The problem with multiple dispatch</h2><p>The single-dispatch design limitation of <code>racket/generic</code> comes directly from a desire to avoid what has been described as “spooky action at a distance”, a problem that is prevalent in many systems that support methods with multiple dispatch (aka <em>multimethods</em>). Specifically, the issue arises when new method implementations are defined for existing datatypes, which can have far-reaching effects throughout a program because the method table is global state. Both CLOS and Clojure suffer from this shortcoming.</p><p>Interestingly, Haskell with multi-parameter typeclasses (a nonstandard but highly useful extension) makes it quite trivial to create constructs similar to multiple dispatch (though the overload resolution is done at compile-time). The similarities are significant: Haskell <em>also</em> suffers from the possibility of a certain sort of “spooky action”. However, Haskell’s static typing and resolution allows the compiler to catch these potential issues, known as “orphan instances”, at compile time. Even though Racket does not support the same sort of static typing, the same idea can be used to keep multiple dispatch safe using the macro system.</p><h2><a name="safe-dynamically-typed-multiple-dispatch"></a>Safe, dynamically-typed multiple dispatch</h2><p>In order to make multiple dispatch safe, we first need to determine exactly what is unsafe. Haskell has rules for determining what constitutes an “orphan instance”, and these rules are equally applicable for determining dangerous multimethod implementations. Specifically, a definition can be considered unsafe if <em>both</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented was declared in a different module from the implementation.</p></li><li><p><em>All</em> of the types used for dispatch in the multimethod instance were declared in a different module from the implementation.</p></li></ol><p>Conversely, a multimethod implementation is safe if <em>either</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></li><li><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></li></ol><p>Why do these two rules provide a strong enough guarantee to eliminate the dangers created by global state? Well, to understand that, we need to understand what can go wrong if these rules are ignored.</p><h3><a name="multimethods-and-dangerous-instances"></a>Multimethods and dangerous instances</h3><p>What exactly is this dangerous-sounding “spooky action”, and what causes it? Well, the trouble stems from the side-effectful nature of multimethod instance definitions. Consider the Racket module from earlier, which defines multiplication instances for scalars and vectors:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Note that there is not actually a <code>(mul vec vec)</code> implementation. This is intentional: there are <em>two</em> ways to take the product of two vectors, so no default implementation is provided. However, it is possible that another module might desire an instance for <code>mul</code> that takes the dot product, and the programmer might write the following definition:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">foldl</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">y</span><span class="p">)))))</span></code></pre><p>However, there is something fishy about the above definition: it doesn’t need to be exported with <code>provide</code> to work! Since instances don’t create new bindings, they only add dispatch options, they don’t ever need to <code>provide</code> anything. This is problematic, though: it means that a program could continue to happily compile <em>even if</em> the module containing the dot product instance was never loaded with <code>require</code>, but an attempt to multiply two vectors would fail at runtime, claiming that there was no <code>(mul vec vec)</code> implementation. This drastic change of behavior violates Racket programmers’ assumptions about the guarantees made by modules (<code>require</code> should not cause any side-effects if the module’s bindings are not used).</p><p>Of course, while this seems potentially unexpected, it is workable: just be careful to <code>require</code> modules containing instances. Unfortunately, it gets much worse—what if a different library defines <em>its own</em> <code>(mul vec vec)</code> instance? What if that instance takes the cross product instead? That library may function entirely properly on its own, but when loaded alongside the program that defines a dot product instance, it is impossible to determine which instance should be used where. Because <code>define-instance</code> operates by modifying the aforementioned global state, the implementations clash, and the two systems <em>cannot</em> continue to operate together as written.</p><p>This is pretty bad. Defining extra instances is a reasonable use-case for multiple dispatch, but if these instances can break <em>third-party code</em>, how can they be trusted? This sort of problem can make multiple dispatch difficult to reason about and even more difficult to trust.</p><h3><a name="what-determines-safety"></a>What determines safety?</h3><p>With those problems in mind, we can turn back to the two rules for <em>safe</em> multiple dispatch. How do they prevent the above issues? Well, let’s take them one at a time.</p><p>Remember that an instance can be unequivocally determined to be safe if either of the two conditions are true, so we can consider them entirely independently. The first one is simple—an instance is safe if the following condition holds:</p><blockquote><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></blockquote><p>This one is pretty obvious. It is impossible to create a “bad” instance of a method declared in the same module because it is impossible to import the method without also bringing in the instance. Furthermore, a conflicting instance cannot be defined at the place where the types themselves are defined because that would require a circular module dependency, which Racket does not permit.</p><p>With the above explanation in mind, the second condition should make sense, too:</p><blockquote><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></blockquote><p>The same argument for the first point holds for the second, but with the parties swapped. Again, it is impossible to use the instance without somehow requiring the module that defines the datatype itself, so the instance would always be required, anyway. The most interesting aspect of this condition is that it demonstrates that instances can be defined for existing datatypes (that are defined in other modules) just so long as <em>at least one</em> of the datatypes is defined in the same module. This continues to permit the important use-case of extending the interfaces of existing types.</p><h3><a name="encoding-the-safety-rules-into-racket-s-macro-system"></a>Encoding the safety rules into Racket’s macro system</h3><p>In order to keep track of which methods and instances are defined where, I leveraged a technique based on the one <a href="http://www.ccs.neu.edu/racket/pubs/scheme2007-ctf.pdf">used by Typed Racket to keep track of whether or not a typed identifier is used in a typed or untyped context</a>. However, instead of using a simple mutable boolean flag, I used a mutable <a href="http://docs.racket-lang.org/syntax/syntax-helpers.html#%28tech._identifier._set%29">free identifier set</a>, which keeps track of the identifiers within a given module that should be considered “privileged”.</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/id-set</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mark-id-as-privileged!</span> +<span class="w"> </span><span class="n">id-privileged?</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="p">(</span><span class="n">mutable-free-id-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-add!</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-member?</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span></code></pre><p>Making this work with <code>define-generic</code> is obvious: just invoke <code>mark-id-as-privileged!</code> on the method name to note that the method is “privileged” in the scope of the current module. Keeping track of privileged structs is similarly straightforward, though it is a little more devious: the <code>multimethod</code> module provides a custom <code>struct</code> macro that just expands to <code>struct</code> from <code>racket/base</code>, but adds privilege information.</p><p>The <code>define-instance</code> macro does all the heavy lifting to ensure that only privileged identifiers can be used in instance definitions. A simple check for the identifier annotations is performed before proceeding with macro expansion:</p><pre><code class="pygments"><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">types</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">types</span><span class="p">)))</span></code></pre><p>When the privilege checks fail, an error is raised:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">)))</span></code></pre><p>With the above safeguards in place, the dangerous dot product implementation from above <strong>would not be allowed</strong>. The checks manage to encode both of the safety rules into the macro system such that invalid instances will fail <em>at compile time</em>, preventing dangerous uses of multimethods from ever slipping by unnoticed.</p><h3><a name="actually-implementing-multiple-dispatch"></a>Actually implementing multiple dispatch</h3><p>The rest of the multimethod implementation is relatively straightforward and is not even particularly robust. If anything, it is the bare minimum of what would be needed to allow the safety mechanisms above to work. Lots of features that would likely be needed in a real implementation are not included, and graceful error handling is largely ignored.</p><p>Multimethods themselves are implemented as Racket <a href="http://docs.racket-lang.org/guide/proc-macros.html#%28tech._transformer._binding%29">transformer bindings</a> containing custom data, including a reference to the multimethod’s arity and dispatch table. The custom datatype includes a <code>prop:procedure</code> structure type property, which allows such bindings to also function as macros. The macro procedure expands to an operation that looks up the proper instance to use in the multimethod’s dispatch table and invokes it with the supplied arguments.</p><p>The relevant code for defining multimethods is reproduced below:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="n">arity</span><span class="w"> </span><span class="n">dispatch-table</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="n">method</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">method</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="n">args</span><span class="p">))]))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-generic</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method:id</span><span class="w"> </span><span class="n">arg:id</span><span class="w"> </span><span class="n">...+</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">arity</span><span class="w"> </span><span class="p">(</span><span class="nb">length</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">arg</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="nb">make-hash</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod</span><span class="w"> </span><span class="n">arity</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">dispatch-table</span><span class="p">))))]))</span></code></pre><p>The dispatch tables are implemented entirely in terms of Racket’s structure types, so while they can be defined on arbitrary structure types (including ones defined in the Racket standard library), they <em>cannot</em> be defined on primitives such as pairs or vectors. Implementations are registered in the dispatch table using the compile-time information associated with structs’ transformer bindings, and the same information is retrieved from struct instances at runtime to look up the proper implementation to call. Notably, this only works if the struct is <code>#:transparent</code>, or more generally and accurately, if the calling code has access to the struct’s inspector. All structs defined by the <code>struct</code> form from the <code>multimethod</code> module are automatically marked as <code>#:transparent</code>.</p><p>The following code implements defining multimethod instances:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-instance</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="c1">; standard (define (proc ...) ...) shorthand</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">((</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; full (define proc lambda-expr) notation</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="n">proc:expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod-dispatch-table</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">compose1</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="n">extract-struct-info</span><span class="w"> </span><span class="nb">syntax-local-value</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))])</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">struct-types</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">hash-set!</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="n">struct-types</span><span class="w"> </span><span class="n">proc</span><span class="p">))))]))</span></code></pre><p>The resulting implementation is a useful, if certainly incomplete implementation of multimethods in Racket that does not sacrifice the safety provided by <code>racket/generic</code>’s single-dispatch approach.</p><h2><a name="related-work-advantages-and-disadvantages-and-areas-for-future-improvement"></a>Related work, advantages and disadvantages, and areas for future improvement</h2><p>As previously mentioned, this implementation of multiple dispatch was inspired by the types of APIs offered by CLOS and Clojure while also maintaining the safety of <code>racket/generic</code>. The inspiration for the safety rules came from GHC’s detection of orphan instances. Although most of the ideas presented above exist in other places, I am unsure if the concept of safety checking has been used before in any dynamically-typed programming languages.</p><p>The primary advantage offered over Racket’s existing generics system is obvious: multiple dispatch. Furthermore, this system can supersede many uses of <code>racket/generic</code> simply by dispatching on a single type. However, the current implementation does <em>not</em> support all of the features of <code>racket/generic</code>, such as supporting non-structure types and allowing fallback implementations. While those are well within the realm of possibility, other things like attaching structure type properties are probably not possible with this approach, so it is unlikely that the existing system could be subsumed by one like this one.</p><p>Additionally, this implementation would almost certainly need numerous improvements before being useful to most programmers:</p><ul><li><p><strong>Good error reporting for failure cases.</strong> Right now, even something obvious like calling a method on values that do not implement it simply fails with an error produced by <code>hash-ref</code>. In a more interesting sense, using the arity to generate compile-time error messages for <code>define-instance</code> would be a nice improvement.</p></li><li><p><strong>Support for Racket primitive data types.</strong> This might require some cooperation from Racket itself to permit an elegant implementation, but they could also just be special-cased. So long as lookup for primitives was done <em>after</em> consulting the main dispatch table, there wouldn’t be any performance hit for non-primitive types.</p></li><li><p><strong>Option to supply fallback implementations.</strong> This wouldn’t be too hard at all, though it’s questionable whether or not it would be useful without method groupings like <code>define/generic</code> provides. There would likely also need to be some sort of way to check if a set of values implements a particular method.</p></li><li><p><strong>Better cooperation with structure inspectors to alleviate the need for all structures to be transparent.</strong> It’s currently unclear to me how exactly this works and how it <em>should</em> work. There might be a better way to do this without mucking with inspectors.</p></li><li><p><strong>Much more flexible argument lists, including the ability to specify arguments that are not used for dispatch.</strong> This is really a pretty fundamental requirement, but the parsing required was significant enough for me to put it off for this initial prototype.</p></li><li><p><strong>Scribble forms to document generic methods and their instances.</strong> This is something <code>racket/generic</code> <em>doesn’t</em> have, and it has suffered for it. It would be very nice to have easy documentation forms for multimethods.</p></li><li><p><strong>Proper consideration of struct subtyping.</strong> Racket structs support subtyping, which I have not given much thought for this prototype. It is possible that subtyping violates constraints I had assumed would hold, so reviewing the existing code with that context would be useful.</p></li></ul><p>I’m not sure how much effort is involved in most of the above ideas, and in fact I’m not even completely sure how useful this system is to begin with. I have not found myself reaching much for multiple dispatch in my time as a Racket programmer, but that could simply be because it was previously unavailable. It will be interesting to see if that changes now that I have built this system, even if it is a bit rough around the edges.</p><h2><a name="conclusion"></a>Conclusion</h2><p>Despite the lack of need for multiple dispatch to solve most problems, as indicated by its general lack of support in mainstream programming languages, it’s a nice tool to have in the toolbox, and it <em>is</em> asked for in the Racket community from time to time (perhaps due to its familiarity in other parts of the Lisp world). Time will tell if pointing people to something like this will create or stifle interest in multiple dispatch for Racket.</p><p>The source for the <a href="https://github.com/lexi-lambda/racket-multimethod"><code>multimethod</code> package can be found here</a> if you are at all interested in playing with it yourself.</p><ol class="footnotes"></ol></article>ADTs in Typed Racket with macroshttps://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/https://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/21 Dec 2015<article><p>Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate <em>why</em> macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is <strong>ADTs</strong>.</p><h2><a name="warning-this-is-not-a-macro-tutorial"></a>Warning: this is not a macro tutorial</h2><p>First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you <em>are</em> interested in learning about macros, I must recommend Greg Hendershott's <a href="http://www.greghendershott.com/fear-of-macros/">Fear of Macros</a>. It is good. This is not that.</p><p>Now, with that out of the way, let's get started.</p><h2><a name="what-we-re-building"></a>What we&rsquo;re building</h2><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data types</a>, or <em>ADTs</em>, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.</p><p>Racket also already has a facility for creating custom data structures in the form of <em>structs</em>, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.</p><p>With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="kt">Tree</span></code></pre><p>This already demonstrates a few of the core things we'll need to build:</p><ol><li><p>Each ADT has a <em>data type</em>, in this case <code>Tree</code>. This name only exists in the world of types, it isn't a value.</p></li><li><p>Each ADT has various <em>data constructors</em>, in this case <code>Leaf</code> and <code>Node</code>.</p></li><li><p>Each data constructor may accept any number of arguments, each of which have a specific type.</p></li><li><p>The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.</p></li></ol><p>Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>With this in mind, we can add a fifth and final point to our list:</p><ol start="5"><li><p>ADTs must be able to be parametrically polymorphic.</p></li></ol><p>That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.</p><h3><a name="describing-adts-in-racket"></a>Describing ADTs in Racket</h3><p>How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>This looks pretty good. Just like with the Haskell implementation, <code>Tree</code> should only exist at the type level, and <code>Empty</code>, <code>Leaf</code>, and <code>Node</code> should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be <code>(Leaf 7)</code>.</p><p>Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's <code>match</code>[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">tree-sum</span><span class="w"> </span><span class="p">((</span><span class="n">Tree</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">tree</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">tree</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Node</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">l</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">r</span><span class="p">))]))</span></code></pre><p>Given that Racket's <code>struct</code> form automatically produces identifiers that cooperate with <code>match</code>, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.</p><h2><a name="implementing-adts-as-syntax"></a>Implementing ADTs as syntax</h2><p>Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. The <code>syntax/parse</code> library works like the traditional Scheme <code>syntax-case</code> on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.</p><p>Since this is not a macro tutorial, the following implementation assumes you already know how to use <code>syntax/parse</code>. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.</p><h3><a name="parsing-types-with-a-syntax-class"></a>Parsing types with a syntax class</h3><p>To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like <code>Tree</code>, or they can be identifiers with parameters, like <code>(Tree a)</code>. We'll want to cover both cases.</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">))))</span></code></pre><p>This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (<code>...+</code>) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use <code>#:attr</code> to give the <code>param</code> attribute the default value of an empty list, so this syntax class will actually <em>normalize</em> the input we get in addition to actually parsing it.</p><h3><a name="a-first-attempt-at-define-datatype"></a>A first attempt at <code>define-datatype</code></h3><p>Now we can move on to actually implementing <code>define-datatype</code>. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using <code>syntax-parser</code>, which actually does the parsing for our macro.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">]))</span></code></pre><p>This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with <code>_</code> (which will just be the identifier <code>define-datatype</code>), then expecting a <code>type-name</code>, which uses the <code>type</code> syntax class we defined above. Next, we expect zero or more <code>data-constructor</code>s, which also use the <code>type</code> syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.</p><p>Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">([</span><span class="n">f</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))]))</span></code></pre><p>This is actually really close to being correct. This will generate a struct definition for each <code>data-constructor</code>, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have <em>names</em>, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for <em>all</em> the fields, <code>f</code>, so if any data constructor has two or more fields, we'll get an error.</p><p>Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called <code>generate-temporary</code>, which generates random identifiers. Our next attempt might look like this:</p><pre><code class="pygments"><span class="o">#`</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="o">#,</span><span class="p">(</span><span class="n">generate-temporary</span><span class="p">)</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>The <code>#,</code> lets us "escape" from the template to execute <code>(generate-temporary)</code> and interpolate its result into the syntax. Unfortunately, this doesn't work. We <em>do</em> generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.</p><h3><a name="more-leveraging-syntax-classes"></a>More leveraging syntax classes</h3><p>As it turns out, this is <em>also</em> easy to do with syntax classes. We can add an extra attribute to our <code>type</code> syntax class to generate a random identifier with each one. Again, we can use <code>#:attr</code> to do that automatically. Our new definition for <code>type</code> will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span></code></pre><p>Here we're using <code>generate-temporaries</code> instead of <code>generate-temporary</code>, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each <code>param</code>.</p><p>We can now fix our macro to use this <code>field-id</code> attribute instead of the static field name:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><h3><a name="creating-the-supertype"></a>Creating the supertype</h3><p>We're almost done—now we just need to implement our overall type, the one defined by <code>type-name</code>. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">Tree</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">Leaf</span><span class="w"> </span><span class="n">Node</span><span class="p">))</span></code></pre><p>However, a polymorphic type alias would need to include the type parameters in each subtype, like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using <strong>procedural macros</strong>.</p><p>To build each properly-instantiated type, we'll use a combination of <code>define/with-syntax</code> and Racket's list comprehensions, <code>for/list</code>. The <code>define/with-syntax</code> form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by <code>syntax-parser</code>. This will allow us to break up our result into multiple steps. Technically, <code>define/with-syntax</code> is not strictly necessary—we could just use <code>#`</code> and <code>#,</code>—but it's cleaner to work with.</p><p>We'll start by defining a set of instantiated data constructor types, one per <code>data-constructor</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span></code></pre><p>Now with our definition for <code>data-type</code>, we can implement our type alias for the supertype extremely easily:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><h3><a name="putting-it-all-together"></a>Putting it all together</h3><p>There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by <code>type-name</code> are in scope for each data constructor's structure definition. We can do this by making use of <code>type-name.param</code> within each produced struct definition, resulting in this:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><p>And we're done! The final macro, now completed, looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span> + +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span><span class="w"> </span><span class="k">...</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">)))]))</span></code></pre><p>It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.</p><h2><a name="using-our-adts"></a>Using our ADTs</h2><p>With the macro built, we can now actually use our ADTs using the syntax we described! The following is now <em>valid code</em>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span> +<span class="nb">-</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">Positive-Byte</span><span class="p">)</span> +<span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span></code></pre><p>We can use this to define common data types, such as Haskell's <code>Maybe</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Nothing</span><span class="p">)</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-default</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-default</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-then</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-then</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Nothing</span><span class="p">)]))</span></code></pre><p>And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="n">Expr</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="n">Number</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Expr</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Number</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">e</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Value</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Add</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">-</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Divide</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">/</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">7</span><span class="p">))))</span> +<span class="mi">4</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, <a href="https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e">I've put together a gist here</a>.</p><h2><a name="conclusions-and-credit"></a>Conclusions and credit</h2><p>This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly <em>are</em> powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.</p><p>This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.</p><p>That said, I think it's pretty cool.</p><p>Finally, I must give credit where credit is due. Thanks to <a href="http://andmkent.com">Andrew M. Kent</a> for the creation of the <a href="https://github.com/andmkent/datatype">datatype</a> package, which served as the inspiration for this blog post. Many thanks to <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> and <a href="http://www.ccs.neu.edu/home/matthias/">Matthias Felleisen</a> for their work on creating <code>syntax/parse</code>, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.</p><p>Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/meta.atom.xml b/feeds/meta.atom.xml new file mode 100644 index 0000000..4180991 --- /dev/null +++ b/feeds/meta.atom.xml @@ -0,0 +1,79 @@ +Posts tagged ‘meta’ | Alexis King’s Blog2015-07-18T00:00:00ZAutomatically deploying a Frog-powered blog to GitHub pages2015-07-18T00:00:00Z2015-07-18T00:00:00ZAlexis King<article><p>So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses <a href="http://www.greghendershott.com">Greg Hendershott</a>'s fantastic <a href="https://github.com/greghendershott/frog">Frog</a> tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via <a href="https://travis-ci.org">Travis CI</a>, so my blog is always up-to-date.</p><h2><a name="setting-up-frog"></a>Setting up Frog</h2><p>I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple <code>raco pkg install frog</code> followed by <code>raco frog --init</code> and <code>raco frog -bp</code> created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is <code>raco frog -n "Post Title"</code>, and you're good to go.</p><p>By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use <a href="http://sass-lang.com">Sass</a> for my stylesheets, potentially with support for <a href="http://coffeescript.org">CoffeeScript</a> later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used <a href="http://gulpjs.com">Gulp</a> in conjunction with <a href="https://www.npmjs.com">NPM</a> for build and dependency management.</p><p>Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.</p><h2><a name="configuring-automatic-deployment-with-travis"></a>Configuring automatic deployment with Travis</h2><p>Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being <a href="https://gist.github.com/domenic/ec8b0fc8ab45f39403dd">this Gist</a>, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special <code>gh-pages</code> branch.</p><p>To make this easy, Frog can be configured to output to a separate directory via the <code>.frogrc</code> configuration file. I chose to output to the <code>out</code> directory:</p><pre><code>output-dir = out +</code></pre><p>I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.</p><pre><code>$ cd out +$ git init +$ git add . +$ git commit -m "Deploy to GitHub Pages" +$ git push --force "$REMOTE_URL" master:gh-pages +</code></pre><p>The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's <a href="http://docs.travis-ci.com/user/encryption-keys/">encryption keys</a> along with a GitHub <a href="https://github.com/settings/tokens">personal access token</a>. Just install the Travis CLI client, copy the access token, and run a command:</p><pre><code>$ gem install travis +$ travis encrypt GH_TOKEN=&lt;access token...&gt; +</code></pre><p>The output of that command is an encrypted value to be placed in an environment variable in the project's <code>.travis.yml</code> configuration file. The URL for the repository on GitHub will also need to be specified as well:</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span></code></pre><p>Now all that's left is configuring the <code>.travis.yml</code> to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to <code>python</code>, then installed Racket and Frog as pre-installation steps.</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span></code></pre><p>(It might be worth noting that Greg Hendershott <em>also</em> maintains the repository that contains the above Travis build script!)</p><p>Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses <code>master</code>, not <code>gh-pages</code>. Obviously, I didn't want Travis running on my <code>master</code> branch, since it would be deploying to that, so I added a branch whitelist:</p><pre><code class="pygments"><span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span></code></pre><p>All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:</p><pre><code class="pygments"><span class="ch">#!/bin/bash</span> +<span class="nb">set</span><span class="w"> </span>-ev<span class="w"> </span><span class="c1"># exit with nonzero exit code if anything fails</span> + +<span class="c1"># clear the output directory</span> +rm<span class="w"> </span>-rf<span class="w"> </span>out<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">0</span><span class="p">;</span> + +<span class="c1"># build the blog files + install pygments for highlighting support</span> +npm<span class="w"> </span>install +npm<span class="w"> </span>run<span class="w"> </span>build +pip<span class="w"> </span>install<span class="w"> </span>pygments +raco<span class="w"> </span>frog<span class="w"> </span>--build + +<span class="c1"># go to the out directory and create a *new* Git repo</span> +<span class="nb">cd</span><span class="w"> </span>out +git<span class="w"> </span>init + +<span class="c1"># inside this git repo we&#39;ll pretend to be a new user</span> +git<span class="w"> </span>config<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Travis CI"</span> +git<span class="w"> </span>config<span class="w"> </span>user.email<span class="w"> </span><span class="s2">"&lt;your@email.here&gt;"</span> + +<span class="c1"># The first and only commit to this new Git repo contains all the</span> +<span class="c1"># files present with the commit message "Deploy to GitHub Pages".</span> +git<span class="w"> </span>add<span class="w"> </span>. +git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"Deploy to GitHub Pages"</span> + +<span class="c1"># Force push from the current repo&#39;s master branch to the remote</span> +<span class="c1"># repo. (All previous history on the branch will be lost, since we are</span> +<span class="c1"># overwriting it.) We redirect any output to /dev/null to hide any sensitive</span> +<span class="c1"># credential data that might otherwise be exposed.</span> +git<span class="w"> </span>push<span class="w"> </span>--force<span class="w"> </span>--quiet<span class="w"> </span><span class="s2">"https://</span><span class="si">${</span><span class="nv">GH_TOKEN</span><span class="si">}</span><span class="s2">@</span><span class="si">${</span><span class="nv">GH_REF</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>master<span class="w"> </span>&gt;<span class="w"> </span>/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span></code></pre><p>For reference, my final <code>.travis.yml</code> looked like this:</p><pre><code class="pygments"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span> +<span class="nt">python</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&#39;3.4&#39;</span> + +<span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span> + +<span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/lexi-lambda/lexi-lambda.github.io.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;long secure token...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span> + +<span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bash ./deploy.sh</span></code></pre><p>That's it! Now I have a working blog that I can publish just by pushing to the <code>source</code> branch on GitHub.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/meta.rss.xml b/feeds/meta.rss.xml new file mode 100644 index 0000000..fcc02c6 --- /dev/null +++ b/feeds/meta.rss.xml @@ -0,0 +1,79 @@ +Posts tagged ‘meta’ | Alexis King’s BlogPosts tagged ‘meta’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/meta.html18 Jul 201518 Jul 201560Automatically deploying a Frog-powered blog to GitHub pageshttps://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/https://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/18 Jul 2015<article><p>So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses <a href="http://www.greghendershott.com">Greg Hendershott</a>'s fantastic <a href="https://github.com/greghendershott/frog">Frog</a> tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via <a href="https://travis-ci.org">Travis CI</a>, so my blog is always up-to-date.</p><h2><a name="setting-up-frog"></a>Setting up Frog</h2><p>I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple <code>raco pkg install frog</code> followed by <code>raco frog --init</code> and <code>raco frog -bp</code> created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is <code>raco frog -n "Post Title"</code>, and you're good to go.</p><p>By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use <a href="http://sass-lang.com">Sass</a> for my stylesheets, potentially with support for <a href="http://coffeescript.org">CoffeeScript</a> later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used <a href="http://gulpjs.com">Gulp</a> in conjunction with <a href="https://www.npmjs.com">NPM</a> for build and dependency management.</p><p>Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.</p><h2><a name="configuring-automatic-deployment-with-travis"></a>Configuring automatic deployment with Travis</h2><p>Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being <a href="https://gist.github.com/domenic/ec8b0fc8ab45f39403dd">this Gist</a>, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special <code>gh-pages</code> branch.</p><p>To make this easy, Frog can be configured to output to a separate directory via the <code>.frogrc</code> configuration file. I chose to output to the <code>out</code> directory:</p><pre><code>output-dir = out +</code></pre><p>I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.</p><pre><code>$ cd out +$ git init +$ git add . +$ git commit -m "Deploy to GitHub Pages" +$ git push --force "$REMOTE_URL" master:gh-pages +</code></pre><p>The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's <a href="http://docs.travis-ci.com/user/encryption-keys/">encryption keys</a> along with a GitHub <a href="https://github.com/settings/tokens">personal access token</a>. Just install the Travis CLI client, copy the access token, and run a command:</p><pre><code>$ gem install travis +$ travis encrypt GH_TOKEN=&lt;access token...&gt; +</code></pre><p>The output of that command is an encrypted value to be placed in an environment variable in the project's <code>.travis.yml</code> configuration file. The URL for the repository on GitHub will also need to be specified as well:</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span></code></pre><p>Now all that's left is configuring the <code>.travis.yml</code> to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to <code>python</code>, then installed Racket and Frog as pre-installation steps.</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span></code></pre><p>(It might be worth noting that Greg Hendershott <em>also</em> maintains the repository that contains the above Travis build script!)</p><p>Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses <code>master</code>, not <code>gh-pages</code>. Obviously, I didn't want Travis running on my <code>master</code> branch, since it would be deploying to that, so I added a branch whitelist:</p><pre><code class="pygments"><span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span></code></pre><p>All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:</p><pre><code class="pygments"><span class="ch">#!/bin/bash</span> +<span class="nb">set</span><span class="w"> </span>-ev<span class="w"> </span><span class="c1"># exit with nonzero exit code if anything fails</span> + +<span class="c1"># clear the output directory</span> +rm<span class="w"> </span>-rf<span class="w"> </span>out<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">0</span><span class="p">;</span> + +<span class="c1"># build the blog files + install pygments for highlighting support</span> +npm<span class="w"> </span>install +npm<span class="w"> </span>run<span class="w"> </span>build +pip<span class="w"> </span>install<span class="w"> </span>pygments +raco<span class="w"> </span>frog<span class="w"> </span>--build + +<span class="c1"># go to the out directory and create a *new* Git repo</span> +<span class="nb">cd</span><span class="w"> </span>out +git<span class="w"> </span>init + +<span class="c1"># inside this git repo we&#39;ll pretend to be a new user</span> +git<span class="w"> </span>config<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Travis CI"</span> +git<span class="w"> </span>config<span class="w"> </span>user.email<span class="w"> </span><span class="s2">"&lt;your@email.here&gt;"</span> + +<span class="c1"># The first and only commit to this new Git repo contains all the</span> +<span class="c1"># files present with the commit message "Deploy to GitHub Pages".</span> +git<span class="w"> </span>add<span class="w"> </span>. +git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"Deploy to GitHub Pages"</span> + +<span class="c1"># Force push from the current repo&#39;s master branch to the remote</span> +<span class="c1"># repo. (All previous history on the branch will be lost, since we are</span> +<span class="c1"># overwriting it.) We redirect any output to /dev/null to hide any sensitive</span> +<span class="c1"># credential data that might otherwise be exposed.</span> +git<span class="w"> </span>push<span class="w"> </span>--force<span class="w"> </span>--quiet<span class="w"> </span><span class="s2">"https://</span><span class="si">${</span><span class="nv">GH_TOKEN</span><span class="si">}</span><span class="s2">@</span><span class="si">${</span><span class="nv">GH_REF</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>master<span class="w"> </span>&gt;<span class="w"> </span>/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span></code></pre><p>For reference, my final <code>.travis.yml</code> looked like this:</p><pre><code class="pygments"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span> +<span class="nt">python</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&#39;3.4&#39;</span> + +<span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span> + +<span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/lexi-lambda/lexi-lambda.github.io.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;long secure token...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span> + +<span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bash ./deploy.sh</span></code></pre><p>That's it! Now I have a working blog that I can publish just by pushing to the <code>source</code> branch on GitHub.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/personal.atom.xml b/feeds/personal.atom.xml new file mode 100644 index 0000000..5674998 --- /dev/null +++ b/feeds/personal.atom.xml @@ -0,0 +1 @@ +Posts tagged ‘personal’ | Alexis King’s Blog2025-05-29T00:00:00ZA break from programming languages2025-05-29T00:00:00Z2025-05-29T00:00:00ZAlexis King<article><p>This is a blog post I have been considering writing for a long time.</p><p>People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.</p><p>My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.</p><p>This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for <em>me</em>, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why <em>not</em>—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.</p><h2><a name="whence-programming-languages"></a>Whence programming languages</h2><p>I have had a personal fascination with programming languages for as long as I have been using them. I made my first serious forays into programming in the fifth grade, writing (very simple and mostly very bad) Flash games in ActionScript 3. I wrote my first programming language interpreter in my sophomore year of high school, a dynamically-typed, object-oriented Lisp with prototypal inheritance. The interpreter was written in C, and despite it being my first serious endeavor with the language, I used preprocessor macros to implement a form of exceptions in terms of <code>setjmp.h</code>. It is an amusing project in retrospect, given what my design sensibilities would eventually turn out to be, but it at least provides a little insight into how much the subject was already on my mind in my teenage years.</p><p>I have always deeply enjoyed programming. That is no less true now than it was fifteen years ago. As someone with (then untreated) ADHD, the automation of repetitive tasks has always held immediate appeal, but it did not take long to discover I enjoy programming for its own sake. It is inherently delightful to me to direct an infinitely reconfigurable machine towards any end I desire, given only a little thought and some time at a keyboard. Program <em>design</em>, even in the large, was something that immediately appealed to me. It is often as much a challenge of organizing one’s thoughts as it is a matter of translating them to a form the computer can understand, and the former is a challenge I have always found rewarding.</p><p>Even so, as every programmer inevitably discovers, the gap between one’s thoughts and executable code is never so narrow as we would like. I quickly discovered that a distressingly large amount of my time was being spent on the exactly the sort of repetitive task I had hoped to avoid, mechanically typing out reams of boilerplate in order to carry out what seemed to be incredibly simple tasks.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> It felt strange that we, as programmers, could be one of the lucky disciplines equipped with the means to produce our own tools, yet the tools we work with are so frequently unsatisfactory.</p><p>Programming is an astonishingly young discipline. FORTRAN, arguably the first recognizable programming language, is less than seventy years old, and the first stored-program computers are only a decade older. Programmers have been trying to bridge the gap between our conceptualizations of programs and the (often laborious) act of programming for as long as computing has existed. As John Backus said in 1979,</p><blockquote><p>Much of my work has come from being lazy. I didn’t like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs.</p></blockquote><p>In my experience, this sentiment perfectly captures the strange relationship many programming language enthusiasts have with our craft. We enjoy programming so much that we are willing to spend enormous time, thought, and effort working on programming systems precisely to free ourselves from the tortuous burden of writing programs.</p><p>One possible means of reconciling this apparent contradiction would be to hypothesize that programmers like me or like Backus enjoy the <em>results</em> of our programs but not the process of writing them. We want to get things done, and we view the computer as a useful but necessary evil that should get out of the way as much and as expediently as possible. However, although I am sure there are many programmers who do match that description, I do not count myself among them, and I would likewise find it extremely difficult to believe that Backus—a man who dedicated his life to designing ever more sophisticated means of expressing computer programs—was someone fundamentally uninterested in programming.</p><p>On the contrary, I see a great deal of work on programming languages as a struggle—often in vain—by those of us who love programming so much that we wish to free it of its unfortunate imperfections. We strive to remove the impediments and obstacles we face when translating the beautiful, platonic programs we hold in our heads into the comparatively cold, alien language of silicon calculating machines. There is a widespread mythology that programming languages are primarily abstractions over machine code, but this perspective has been misguided for almost as long as programming languages have existed. Programming languages are instruments of thought, frameworks for precisely specifying an executable idea, and they are designed almost entirely for <em>humans</em>, with computers often providing little more than a loose set of inconvenient restrictions.</p><h3><a name="to-compute-is-human-to-program-divine"></a>To compute is human, to program divine</h3><p>The earliest computers were developed with a singular purpose in mind: to compute. A program was, essentially, a sequence of instructions containing the computations to be performed. This conceptualization of a computer program as essentially analogous to a cooking recipe or assembly manual has remained curiously persistent, even to the present day.</p><p>Almost no modern programs of any complexity look anything like a cooking recipe. Even programs written in the most imperative programming languages under the sun are complex webs of logic specifying dataflow, access policy, and interaction patterns between semi-autonomous, reactive subsystems, many of which come equipped with their own living, beating hearts. At the micro scale, it is true that programs specify behavior as a series of steps to be performed, but even this is a fiction maintained by modern compilers for humans’ convenience. Through a compiler’s eyes, the <em>real</em> programs are whatever is left after the comforting fiction is boiled away to obtain a homogenous pile of SSA and dataflow graphs to be haphazardly reassembled into a patchwork of basic blocks in a language most programmers would not even begin to understand.</p><p>Indeed, in stark contrast to the mental model of the programmer as a sort of silicon whisperer, I believe most practicing software engineers are primarily maintaining vast behavioral specifications whose defining sense of rigor comes not from computers <em>per se</em> but from the consistency we demand from their results. In other words, we are trained to be fluent speakers of languages that allow us to articulate a solution to a problem with a level of precision that ensures every legitimate interpretation is and will always be to our satisfaction. Crucially, these specifications do not <em>fully</em> specify how the task is to be carried out. Optimizing compilers are free to make their own choices with whatever leeway we permit them. HTML/CSS user agents may interpret our instructions in whatever way preserves whatever web standards happen to require. Even RDBMS query planners, a foundational technology largely unchanged for the past several decades, are essentially expert systems designed to decide on the fly how best to execute searches and retrievals under ever-changing conditions.</p><p>This is not to say that the task of programming is fully removed from the machine that ultimately executes our instructions, nor is it even really a suggestion it ought to be. Performance and debugging mandate a certain familiarity with the underlying system, even if the full complexities of its true inner workings remain carefully hidden from even its expert users. My point is simply that programs are not rote algorithms, and the act of programming is not primarily characterized by algorithmic thinking. Rather, programming is fundamentally a game of domain modeling and system design, with bits of algorithmic logic inserted here and there to glue the whole thing together.</p><p>My interest in pushing the frontier of programming language design has always been and continues to be an interest in making the task of writing programs feel closer to the task of designing them. In an ideal world, I would have a language expressive enough to more or less directly translate the computer programs I have in my head (which, for me, more often than not take <em>visual</em> form) into a digitized structure a computer may faithfully execute. It is that task I found myself fixated on at the tender age of fifteen, and it is one I would remain fixated on for the better part of the following decade.</p><h2><a name="ten-years-of-programming-languages"></a>Ten years of programming languages</h2><p>I turned twenty eight this year. Almost exactly ten years ago, I started my first professional software engineering job. It was a fairly mundane position writing Ruby on Rails and JavaScript, but even then, I spent a great deal of free time pursuing the programming language hobby projects that truly caught my fancy. My first professional experience working with Haskell would come less than a year later, experience that would soon inspire my hobby research project to implement a Haskell-style type system within the Racket macro system. The majority of my career to date has followed more or less directly from that formative time.</p><p>After a decade, it seems fair to reflect a little on what I have learned, what I managed to achieve, and maybe more significantly, what I did <em>not</em>. Something one learns very quickly upon beginning any serious work on programming languages is that they are startlingly, unforgivably <em>hard</em>. A vast array of dizzyingly smart people have been working on the problem of program specification for seventy years; there are not many pieces of low hanging fruit left on the tree, and any new gains tend to be very hard won.</p><p>Still, even if programming language design is challenging, it would be some consolation if we were able to slowly yet steadily advance the state of the art, gradually picking away at the gap between the tools we have now and the ultimate programming language of the future. Sadly, even that pragmatic vision does not survive contact with reality. In practice, the challenge of programming language design is not one of expanding a well-defined frontier, it is grappling with a neverending list of fundamental <em>tradeoffs</em> between mutually incompatible features.</p><p>Subtyping is tantalizingly useful but makes complete type inference incredibly difficult (and in general, provably impossible). Structural typing drastically reduces the burden of assigning a <em>name</em> to every uninteresting intermediate form but more or less precludes taking advantage of Haskell-style typeclasses or Rust-style traits. Dynamic dispatch substantially assists decoupling of software components but can come with a significant performance cost without a JIT. Just-in-time compilation can use runtime information to optimize code in ways that permit more flexible coding patterns, but JIT performance can be unpredictable, and the overhead of an optimizing JIT is substantial. Sophisticated metaprogramming systems can radically cut down on boilerplate and improve program concision and even readability, but they can have substantial runtime or compile-time overhead and tend to thwart automated tooling. The list goes on and on.</p><p>The essential, most stubborn problems in programming languages come from unavoidable tensions between conflicting desires and requirements. We want loosely coupled software components that can be easily reused, but we also want the performance benefits of tight coupling and specialization. We want flexible programming languages that do not impose upon our freedom of expression, but we also want the benefits of static program analysis and powerful safety guarantees. We want sophisticated type systems that allow specifying ever more complex invariants, but we also want readable type signatures that won’t regularly end up longer than the code itself.</p><p>We cannot build the One True Programming Language because we cannot have everything at once, and we cannot hope to universally decide which tradeoffs are the right ones to make. This naturally makes the thought of a system constructed from a tapestry of many different programming languages attractive, affording the programmer the opportunity to select the tool best suited to the task at hand. Unfortunately, that, too, comes with (often brutal) tradeoffs of its own: the impedance mismatch at language boundaries, the vastly more complex tooling required for a polyglot system, and the increased knowledge burden of maintaining a system built in so many different ways.</p><p>Ten years of working on and thinking about programming languages has forced me to come to terms with a humbling truth: I do not know how to build a better programming language. I am personally extremely sympathetic to the idea that the future of programming probably involves more domain-specific languages, and I think the industry as a whole has been (very slowly) trending in that direction for quite some time. But even I acknowledge that the question of how precisely that ought to be done is extraordinarily complicated, and there are no easy answers here.</p><p>Even so, it would be far too dismal of me to suggest that the problem is hopeless, nor do I believe the work does not matter. Even if the problem has proven more difficult than teenage me may have hoped, the work <em>is</em> satisfying when it bears fruit. Unfortunately, even after toiling for years overcoming daunting problems, every working programming language enthusiast is forced to confront a much more frustrating enemy: <em>programmers</em>.</p><h3><a name="the-reactionary-conservatism-of-the-median-programmer"></a>The reactionary conservatism of the median programmer</h3><p>It should come as no surprise that programming language design is largely a social problem. Choice of programming language tends to be driven by strong network effects, and programmers tend to prefer languages that resemble what they already know. Programmers’ general lack of curiosity and outright hostility to learning new things has always been a personal frustration of mine, but I ultimately do understand much of where it comes from: as engineers, an overwhelming part of our job description is managing <em>risk</em>. Exotic technology choices are risky, and it is a responsible impulse to be wary of them.</p><p>Still, the history of mainstream programming languages is essentially a story of programmers vocally and emphatically rejecting what eventually proved to be some of the most incredibly successful innovations in the history of the field. Assembly programmers largely laughed at FORTRAN, but just a few decades later, there were nevertheless very few remaining assembly programmers. First-class functions were widely derided as needlessly complicated and confusing until programmers were forced to finally take the time to learn to use them once JavaScript became a load-bearing language by historical accident, and within a decade, they became a required feature for every major programming system. Sophisticated type systems largely retain a perception of overengineered, ivory-tower elitism, but many of the programmers who hold those very opinions have enthusiastically adopted Rust, a language that features a type system so complex that idiomatic Rust code can easily put Haskell programs to shame.</p><p>Discussions of programming languages are, by and large, emotionally driven shouting matches based on anecdotes and gut feelings. Surprisingly (or perhaps not), although most working programming language theorists and compiler engineers have a far more informed perspective on this subject (and do at least tend to refrain from petty debate), disparaging remarks directed towards languages not to the speaker’s taste are hardly unheard of even at academic conferences dedicated to the subject. I want to be very clear that I do not think that is somehow indicative of some toxicity within the community—they are by and large a group of truly amazing, wonderful people, and such comments are usually delivered with tongue planted firmly in cheek. All the same, my point is simply that programming languages are an emotional, opinionated subject in a way that seems perhaps inherent to the craft, <a href="/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/">a topic I explored at some length all the way back in 2019.</a></p><p>I do earnestly believe that the field of software engineering would benefit from more openness to new ideas and willingness to experiment with new technologies. I also think it would be enormously refreshing if programmers did not nearly universally speak with an expert’s confidence on subjects they have only passingly encountered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> Nevertheless, I do not expect programmers’ tribalism to diminish anytime soon, so the pragmatist in me understands their nature imposes real limitations on what we as a field can meaningfully accomplish.</p><p>In my experience, most working programming language researchers have essentially resigned themselves to this fact, and the prevailing wisdom is to almost entirely decouple the value of the work from its impact. Even if an idea is broadly ignored by programmers today, if it’s a good enough idea, it will still make its way into some programming language in some distant tomorrow, as many ideas eventually have. Lexical closures were first applied to programming languages in 1975 but took a full thirty years to be broadly accepted. Algebraic datatypes were first introduced around the same time and have only recently found mainstream acceptance through Rust. Innovations take a long, <em>long</em> time to make the jump from research to practice in this field, and placing one’s sense of professional accomplishment in the fickle hands of mainstream programmers’ whims and preferences is a recipe for personal misery.</p><p>I have nothing but respect for this attitude, genuinely. This research community is astonishingly friendly, warm, creative, fun, and accepting, especially given the vitriol these subjects tend to elicit from programmers at large. I feel genuinely honored to have had the opportunity to personally collaborate with many of them, and the collective output of endlessly fascinating ideas and exciting projects one can find each and every year at every major PL conference is consistently inspiring. All the same, as much respect as I have for the people who work tirelessly on these problems for their own sake, I am not sure I can continue to count myself among them. I do find the subject exciting, and I do consider it rewarding in its own way, but these past few years, I have slowly been coming to terms with the fact that I don’t think I find it quite rewarding enough.</p><h2><a name="why-i-write-computer-programs"></a>Why I write computer programs</h2><p>I like writing software. It is strange how radical a statement that sometimes feels. So many programmers seem to practically despise what they do for a living, at least as you’d hear them describe it. I have never really been able to empathize with that mindset. I find programming fun, and I derive a unique joy from writing code that decisively solves the problem at hand. It doesn’t have to be a particularly interesting problem, and the solution doesn’t even have to require much more than snapping blocks together, but as long as I can take some pride and satisfaction in the craftsmanship and end result, I am usually pretty happy.</p><p>My attraction to libraries, tooling, and compilers has always stemmed from a mixture of personal motivation and a certain satisfaction from improving things for my peers. One of my favorite things about software engineering is taking a problem and solving it in such a way that it largely <em>stays solved</em>. Sure, it may need to change as requirements evolve, but the solution continues to work even after the labor is done. Other tasks in my life do not provide nearly the same sense of accomplishment. When I do a load of laundry, it provides me with clean clothes for a week or two, but ultimately, I know it won’t be long before I’m going to have to do it again. Software engineering is not like that. Working on software—even extremely mundane software—provides a pleasant variety of tasks that can be well and truly <em>completed</em>. I like that sense of progress, and I appreciate the relaxing, almost meditative process of growing and maintaining the metaphorical garden that is a collection of code.</p><p>Given all of the above, it is not altogether clear why I would struggle to take satisfaction in my work on programming languages. If anything, working on a compiler seems like the ultimate realization of my aforementioned desires applied to the very thing I, myself, spend my day doing. If there is some annoying or repetitive task, and I can extend my programming system to provide a better way of doing it, that task is something I will never again need to do. What’s more, working on a compiler that thousands of people actually use means this is not just benefiting me, it’s benefiting everyone else as well. It feels good to have that kind of impact, and to know that my effort is essentially multiplied by everyone who happens to use it.</p><p>On paper, this ought to make working on a compiler like GHC enormously satisfying. Even if I feel frustrated by mainstream attitudes towards programming languages, Haskell programmers feel differently. Surely, working on something used and appreciated by Haskellers ought to feel very rewarding. But it wasn’t really. There were parts that were nice, yes, and the problems were often interesting. But I didn’t find it <em>rewarding</em> in the way I had hoped, and I’m not sure it even compared favorably to working on bog-standard software. Why?</p><h3><a name="on-the-haskell-community"></a>On the Haskell community</h3><p>This was the hardest section of this blog post to write. It is easy to write about the things I love and the people I admire. It is perhaps even easier to write about the things I despise and the people I disdain. The Haskell community is neither. I do not <em>dislike</em> it. Several of its members are people I consider very good friends. Nonetheless, I have never managed to fall in any particular love with it, and even if that is no sin on its part, it is a somewhat sad thing to have to confess.</p><p>I want to be <em>excruciatingly</em> clear that I do not wish to mount any indictment of the Haskell community or any of the people in it. Many people seem to have a preconception of Haskellers as mean and elitist, and while I do get the sense there have been some historically bad actors that contributed to that perception, my general impression is that those people have been thoroughly expunged. I have never been the target of any memorably rude conduct, and I’ve definitely never felt targeted by any prejudice or discrimination, and if anything I have always felt universally warmly welcomed at every meetup or conference I have attended. I cannot speak for anyone but myself, and my experiences are necessarily limited. I cannot hope to claim with any certainty that no abuse has ever occurred or that it does not continue to occur within any Haskell’s myriad social spaces, most of which I have never participated in. Still, I am happy to be able to say that I have never personally experienced any real unfriendliness, and that is certainly not a factor in my dispassion for the language’s community. If anything, I feel I must extend a very genuine <em>thank you</em> for all the lovely support I have received from Haskellers over the years, and it is that support and enthusiasm that so often kept me motivated for as long as I was.</p><p>This is all simply to say that I do not think it is the <em>fault</em> of anyone in particular for my lack of affinity for the Haskell community. Rather, I think perhaps it is just not a culture that I have ever especially related to. That probably does not even say very much, as I’m not sure there are many communities I <em>could</em> say that about. That says much more about me than it does about Haskell or Haskellers.</p><p>It is admittedly tempting to say that perhaps I find these communities alienating in part because they lack people I identify with and relate to. After all, it is simply true that the Haskell community is <em>overwhelmingly</em> male, which really has felt lonely at times. It’s also true that the Haskell community tends to attract a lot of people who are mainly interested in Haskell because they find it intellectually interesting, or because they like category theory and enjoy libraries like <code>lens</code> that have explored those ideas, while I am really only interested in Haskell insofar as it is a practical vehicle for writing useful software. Nevertheless, there <em>are</em> other women who write Haskell and there <em>are</em> other people who care very much about writing useful software in Haskell, and that doesn’t seem to have radically altered my relationship to the community, so I think it would be disingenuous of me to claim that either of those flaws are substantially to blame.</p><p>If there is one substantive frustration I can express with Haskell, it is where the money comes from. For as long as I have been involved with it, Haskell has predominantly been used and funded by a combination of fintech and cryptocurrency. I don’t really have anything against fintech, though I certainly don’t find it especially exciting, but I do have something against crypto, and those who followed me on Twitter before its untimely demise know I have always been pretty open about that fact. My thoughts on the matter are really not something I want to elaborate on here, but suffice to say it is not especially motivating to work on a language if all of its users are applying it in ways I feel essentially indifferent about at best and actively hostile to at worst. This is obviously not something I have any easy answers to, but it is probably the most significant, concrete factor in me losing my interest in working on GHC, so if you mourn me leaving, at least I can give you one semi-actionable reason.</p><p>Make no mistake: GHC is a truly incredible project that often still feels like a compiler from the future, and having the opportunity to work on it as a part of my job was about as technologically stimulating as I could possibly ask for. Even so, during my time at Tweag, I often found myself reflecting on the fact that the <em>users</em> I was working for were, ultimately, Haskell programmers, a class I’m not sure even really contains <em>me</em>. I have often reflected on the fact that, although I have many personal projects, I have <em>never</em> chosen Haskell for any of them. Not once. Whenever I need to write a little code to accomplish some task, it’s always Racket. This blog is in Racket. My personal shell utilities are in Racket. When I want to scrape a website or wrap some library, I do it with Racket. Haskell is a language I have always exclusively written professionally, and although I do very earnestly love many things about it, it is not so precious to me that I feel motivated to contribute to it out of passion alone. If there were more <em>users</em> of Haskell doing inspiring, exciting things with the tools I was working on, I might feel substantially more driven to indirectly contribute to those causes. But Haskell is a niche language used by a select few, and it is hard for me to spend so much of my life working on a technological marvel that practically nobody will ever benefit from.</p><p>As a final note on this subject: I do believe very earnestly that the functional programming and programming languages communities would benefit enormously from more demographic diversity. Even compared to software engineering as a whole, the gender ratio is, frankly, almost unbelievably grim. I have always been drawn to interests and hobbies that tend to skew male, and that has never really bothered me, so if <em>I</em> have found myself feeling alienated by the conspicuous absence of other women—to the point that, in the past, I have sometimes considered giving up on Haskell primarily for that reason—I don’t think it is outrageous to say things are pretty dire. A wider set of perspectives could, in theory, inject some refreshing creativity into the dreary ecosystem that is industrial Haskell. Of course, I will admit that I have absolutely no idea how that end could possibly be achieved, and it is a subject far broader and more complicated than I think belongs in this blog post. For now, I will leave things at that, but I hope my voice is sufficiently well regarded that some readers may take my comments to heart.</p><h2><a name="reflections-on-a-decade-s-work"></a>Reflections on a decade’s work</h2><p>Before writing this blog post, I essentially pitched it to a group of friends as “a sort of apology”. At times I certainly feel burdened by the weight of all the endeavors I’ve left unfinished, all the work I never carried long enough to bear fruit. Even so, as I write these words, I find myself reflecting on the things I feel I <em>did</em> manage to contribute these past ten years, and it strikes me that the trail I’ve left behind is perhaps not so dismal, after all.</p><p>It is comforting to look back on a decade’s accumulations and realize there is enough to take some real pride in. I am proud of my contributions to Racket and Haskell, and of my numerous libraries in each of them. I am proud of my experiments, among them <a href="https://github.com/lexi-lambda/hackett">Hackett</a> and <a href="https://github.com/lexi-lambda/eff">eff</a>, and the excellent work from others they have inspired. I am proud of this very blog, from the <a href="/blog/2019/11/05/parse-don-t-validate/">most impactful</a> things I’ve written to the <a href="/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/">far more niche</a>. I am proud of my <a href="https://langdev.stackexchange.com/users/861/alexis-king?tab=answers&amp;sort=newest">contributions to Programming Languages Stack Exchange</a>, a site I most certainly intend to remain a moderator of and will undoubtedly continue to contribute to. And I am proud of the <a href="https://www.youtube.com/watch?v=0jI-AlWEwYI">numerous talks</a> I have <a href="https://www.youtube.com/watch?v=TE48LsgVlIU">presented over the years</a>, all of them labors of love.</p><p>I have not always accomplished all the things I set out to achieve. Some have, for some reason or another, gotten the better of me. Many of them, from my <a href="https://github.com/lexi-lambda/blackboard">math typesetting system</a> to my <a href="https://github.com/ghc-proposals/ghc-proposals/pull/303">improvements to GHC’s arrow notation</a>, are things I hope to one day push over the finish line. But for now it is clear my heart is no longer really in them, so I think it is time for me to let them go. Perhaps others will find inspiration in them. Perhaps you will succeed where I did not.</p><p>It is a little sad to think about moving on from such a substantial chapter of my life. To date, I have spent almost my entire career in this professional niche, and I suspect I will never fully leave it. It has been a pleasure to work with so many bright, lovely, inspiring people, and perhaps one day I will find myself eager to return once more. But for now, my interests are elsewhere, and despite the bittersweet feelings, there is also a very real excitement in the tantalizing opportunity to do something new.</p><h2><a name="what-next"></a>What next?</h2><p>It is a little amusing to think that what I find myself most drawn to after a decade of pursuing ever more exotic projects is something entirely mundane: I want to write utterly ordinary software. I want to work on a piece of software used by people to accomplish a task, and I want to take pride and satisfaction in doing it well.</p><p>I do not yet really have any idea what that might look like. It has been over six years since I last worked on anything that really feels like it could possibly fit that description, and even then, it was only one part of my job. Is it so strange that after spending fifteen years of my life trying everything I could to get away from spending all day wrangling a big ball of mud in a Java IDE, that experience sounds pretty cozy to return to?</p><p>In late 2022, I decided on a whim to reverse engineer and resurrect a 2008 Java browser game called <a href="https://github.com/lexi-lambda/shattered-plans">Shattered Plans</a>. While doing so, I had the opportunity to spend an awful lot of time in IntelliJ, and after years of acclimatizing to writing code in fanciful languages with nothing more than a plain text editor, a terminal emulator, and a dream, the experience was almost viscerally thrilling. They say the grass is always greener, but even if that is true, I think I’d like to find it out for myself.</p><p>More to the point, my life is just very different now from how it was for most of my twenties. I was single, and it was easy to justify spending almost all of my time thinking about code, whether on the clock or not. It was hardly unheard of for me to spend twelve or even sixteen hours at my desk, just tinkering on something that had captured my attention. It was fun, and I’m glad I did it, but I am also very ready to work a simple, regular job I don’t feel the need to permanently devote a large portion of my soul to. It isn’t very romantic, but then, I have other things for that.</p><p>If you think you have a position that meets those (admittedly extremely broad) criteria, and you think you might like to hire me, by all means: <a href="mailto:lexi.lambda@gmail.com">send me an email</a>. The worst I can do is not reply. And who knows? With the burden of my own expectations behind me, perhaps I’ll even blow the dust off this old blog of mine and start writing things again. As with my job, whatever I might choose to write next I cannot say. Either way, it’s a little exciting to be able to treat myself to a blank slate.</p><p>I could probably spend twenty thousand words writing in circles about all my thoughts and feelings on this subject. However, to be honest, I don’t especially want to. As this blog post has hopefully communicated, I would like to be done, and so this will have to be enough. Allow me to personally thank all of you who have indulged me by reading my disorganized ramblings to the end.</p><p>It’s been a long time. Here’s to another ten years.</p><ol class="footnotes"><li id="footnote-1"><p>Readers may find it clarifying to note that a great deal of my early programming experience was writing (then era-appropriate) Java 6. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I have often wondered if other engineering disciplines have anything to rival the collective overconfidence of the average Hacker News commenter. This is an earnest curiosity, not merely a rhetorical flourish; I do sometimes wonder what it is about my field of choice that engenders such disregard for the value of expertise. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/personal.rss.xml b/feeds/personal.rss.xml new file mode 100644 index 0000000..6eebe0c --- /dev/null +++ b/feeds/personal.rss.xml @@ -0,0 +1 @@ +Posts tagged ‘personal’ | Alexis King’s BlogPosts tagged ‘personal’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/personal.html29 May 202529 May 202560A break from programming languageshttps://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/https://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/29 May 2025<article><p>This is a blog post I have been considering writing for a long time.</p><p>People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.</p><p>My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.</p><p>This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for <em>me</em>, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why <em>not</em>—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.</p><h2><a name="whence-programming-languages"></a>Whence programming languages</h2><p>I have had a personal fascination with programming languages for as long as I have been using them. I made my first serious forays into programming in the fifth grade, writing (very simple and mostly very bad) Flash games in ActionScript 3. I wrote my first programming language interpreter in my sophomore year of high school, a dynamically-typed, object-oriented Lisp with prototypal inheritance. The interpreter was written in C, and despite it being my first serious endeavor with the language, I used preprocessor macros to implement a form of exceptions in terms of <code>setjmp.h</code>. It is an amusing project in retrospect, given what my design sensibilities would eventually turn out to be, but it at least provides a little insight into how much the subject was already on my mind in my teenage years.</p><p>I have always deeply enjoyed programming. That is no less true now than it was fifteen years ago. As someone with (then untreated) ADHD, the automation of repetitive tasks has always held immediate appeal, but it did not take long to discover I enjoy programming for its own sake. It is inherently delightful to me to direct an infinitely reconfigurable machine towards any end I desire, given only a little thought and some time at a keyboard. Program <em>design</em>, even in the large, was something that immediately appealed to me. It is often as much a challenge of organizing one’s thoughts as it is a matter of translating them to a form the computer can understand, and the former is a challenge I have always found rewarding.</p><p>Even so, as every programmer inevitably discovers, the gap between one’s thoughts and executable code is never so narrow as we would like. I quickly discovered that a distressingly large amount of my time was being spent on the exactly the sort of repetitive task I had hoped to avoid, mechanically typing out reams of boilerplate in order to carry out what seemed to be incredibly simple tasks.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> It felt strange that we, as programmers, could be one of the lucky disciplines equipped with the means to produce our own tools, yet the tools we work with are so frequently unsatisfactory.</p><p>Programming is an astonishingly young discipline. FORTRAN, arguably the first recognizable programming language, is less than seventy years old, and the first stored-program computers are only a decade older. Programmers have been trying to bridge the gap between our conceptualizations of programs and the (often laborious) act of programming for as long as computing has existed. As John Backus said in 1979,</p><blockquote><p>Much of my work has come from being lazy. I didn’t like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs.</p></blockquote><p>In my experience, this sentiment perfectly captures the strange relationship many programming language enthusiasts have with our craft. We enjoy programming so much that we are willing to spend enormous time, thought, and effort working on programming systems precisely to free ourselves from the tortuous burden of writing programs.</p><p>One possible means of reconciling this apparent contradiction would be to hypothesize that programmers like me or like Backus enjoy the <em>results</em> of our programs but not the process of writing them. We want to get things done, and we view the computer as a useful but necessary evil that should get out of the way as much and as expediently as possible. However, although I am sure there are many programmers who do match that description, I do not count myself among them, and I would likewise find it extremely difficult to believe that Backus—a man who dedicated his life to designing ever more sophisticated means of expressing computer programs—was someone fundamentally uninterested in programming.</p><p>On the contrary, I see a great deal of work on programming languages as a struggle—often in vain—by those of us who love programming so much that we wish to free it of its unfortunate imperfections. We strive to remove the impediments and obstacles we face when translating the beautiful, platonic programs we hold in our heads into the comparatively cold, alien language of silicon calculating machines. There is a widespread mythology that programming languages are primarily abstractions over machine code, but this perspective has been misguided for almost as long as programming languages have existed. Programming languages are instruments of thought, frameworks for precisely specifying an executable idea, and they are designed almost entirely for <em>humans</em>, with computers often providing little more than a loose set of inconvenient restrictions.</p><h3><a name="to-compute-is-human-to-program-divine"></a>To compute is human, to program divine</h3><p>The earliest computers were developed with a singular purpose in mind: to compute. A program was, essentially, a sequence of instructions containing the computations to be performed. This conceptualization of a computer program as essentially analogous to a cooking recipe or assembly manual has remained curiously persistent, even to the present day.</p><p>Almost no modern programs of any complexity look anything like a cooking recipe. Even programs written in the most imperative programming languages under the sun are complex webs of logic specifying dataflow, access policy, and interaction patterns between semi-autonomous, reactive subsystems, many of which come equipped with their own living, beating hearts. At the micro scale, it is true that programs specify behavior as a series of steps to be performed, but even this is a fiction maintained by modern compilers for humans’ convenience. Through a compiler’s eyes, the <em>real</em> programs are whatever is left after the comforting fiction is boiled away to obtain a homogenous pile of SSA and dataflow graphs to be haphazardly reassembled into a patchwork of basic blocks in a language most programmers would not even begin to understand.</p><p>Indeed, in stark contrast to the mental model of the programmer as a sort of silicon whisperer, I believe most practicing software engineers are primarily maintaining vast behavioral specifications whose defining sense of rigor comes not from computers <em>per se</em> but from the consistency we demand from their results. In other words, we are trained to be fluent speakers of languages that allow us to articulate a solution to a problem with a level of precision that ensures every legitimate interpretation is and will always be to our satisfaction. Crucially, these specifications do not <em>fully</em> specify how the task is to be carried out. Optimizing compilers are free to make their own choices with whatever leeway we permit them. HTML/CSS user agents may interpret our instructions in whatever way preserves whatever web standards happen to require. Even RDBMS query planners, a foundational technology largely unchanged for the past several decades, are essentially expert systems designed to decide on the fly how best to execute searches and retrievals under ever-changing conditions.</p><p>This is not to say that the task of programming is fully removed from the machine that ultimately executes our instructions, nor is it even really a suggestion it ought to be. Performance and debugging mandate a certain familiarity with the underlying system, even if the full complexities of its true inner workings remain carefully hidden from even its expert users. My point is simply that programs are not rote algorithms, and the act of programming is not primarily characterized by algorithmic thinking. Rather, programming is fundamentally a game of domain modeling and system design, with bits of algorithmic logic inserted here and there to glue the whole thing together.</p><p>My interest in pushing the frontier of programming language design has always been and continues to be an interest in making the task of writing programs feel closer to the task of designing them. In an ideal world, I would have a language expressive enough to more or less directly translate the computer programs I have in my head (which, for me, more often than not take <em>visual</em> form) into a digitized structure a computer may faithfully execute. It is that task I found myself fixated on at the tender age of fifteen, and it is one I would remain fixated on for the better part of the following decade.</p><h2><a name="ten-years-of-programming-languages"></a>Ten years of programming languages</h2><p>I turned twenty eight this year. Almost exactly ten years ago, I started my first professional software engineering job. It was a fairly mundane position writing Ruby on Rails and JavaScript, but even then, I spent a great deal of free time pursuing the programming language hobby projects that truly caught my fancy. My first professional experience working with Haskell would come less than a year later, experience that would soon inspire my hobby research project to implement a Haskell-style type system within the Racket macro system. The majority of my career to date has followed more or less directly from that formative time.</p><p>After a decade, it seems fair to reflect a little on what I have learned, what I managed to achieve, and maybe more significantly, what I did <em>not</em>. Something one learns very quickly upon beginning any serious work on programming languages is that they are startlingly, unforgivably <em>hard</em>. A vast array of dizzyingly smart people have been working on the problem of program specification for seventy years; there are not many pieces of low hanging fruit left on the tree, and any new gains tend to be very hard won.</p><p>Still, even if programming language design is challenging, it would be some consolation if we were able to slowly yet steadily advance the state of the art, gradually picking away at the gap between the tools we have now and the ultimate programming language of the future. Sadly, even that pragmatic vision does not survive contact with reality. In practice, the challenge of programming language design is not one of expanding a well-defined frontier, it is grappling with a neverending list of fundamental <em>tradeoffs</em> between mutually incompatible features.</p><p>Subtyping is tantalizingly useful but makes complete type inference incredibly difficult (and in general, provably impossible). Structural typing drastically reduces the burden of assigning a <em>name</em> to every uninteresting intermediate form but more or less precludes taking advantage of Haskell-style typeclasses or Rust-style traits. Dynamic dispatch substantially assists decoupling of software components but can come with a significant performance cost without a JIT. Just-in-time compilation can use runtime information to optimize code in ways that permit more flexible coding patterns, but JIT performance can be unpredictable, and the overhead of an optimizing JIT is substantial. Sophisticated metaprogramming systems can radically cut down on boilerplate and improve program concision and even readability, but they can have substantial runtime or compile-time overhead and tend to thwart automated tooling. The list goes on and on.</p><p>The essential, most stubborn problems in programming languages come from unavoidable tensions between conflicting desires and requirements. We want loosely coupled software components that can be easily reused, but we also want the performance benefits of tight coupling and specialization. We want flexible programming languages that do not impose upon our freedom of expression, but we also want the benefits of static program analysis and powerful safety guarantees. We want sophisticated type systems that allow specifying ever more complex invariants, but we also want readable type signatures that won’t regularly end up longer than the code itself.</p><p>We cannot build the One True Programming Language because we cannot have everything at once, and we cannot hope to universally decide which tradeoffs are the right ones to make. This naturally makes the thought of a system constructed from a tapestry of many different programming languages attractive, affording the programmer the opportunity to select the tool best suited to the task at hand. Unfortunately, that, too, comes with (often brutal) tradeoffs of its own: the impedance mismatch at language boundaries, the vastly more complex tooling required for a polyglot system, and the increased knowledge burden of maintaining a system built in so many different ways.</p><p>Ten years of working on and thinking about programming languages has forced me to come to terms with a humbling truth: I do not know how to build a better programming language. I am personally extremely sympathetic to the idea that the future of programming probably involves more domain-specific languages, and I think the industry as a whole has been (very slowly) trending in that direction for quite some time. But even I acknowledge that the question of how precisely that ought to be done is extraordinarily complicated, and there are no easy answers here.</p><p>Even so, it would be far too dismal of me to suggest that the problem is hopeless, nor do I believe the work does not matter. Even if the problem has proven more difficult than teenage me may have hoped, the work <em>is</em> satisfying when it bears fruit. Unfortunately, even after toiling for years overcoming daunting problems, every working programming language enthusiast is forced to confront a much more frustrating enemy: <em>programmers</em>.</p><h3><a name="the-reactionary-conservatism-of-the-median-programmer"></a>The reactionary conservatism of the median programmer</h3><p>It should come as no surprise that programming language design is largely a social problem. Choice of programming language tends to be driven by strong network effects, and programmers tend to prefer languages that resemble what they already know. Programmers’ general lack of curiosity and outright hostility to learning new things has always been a personal frustration of mine, but I ultimately do understand much of where it comes from: as engineers, an overwhelming part of our job description is managing <em>risk</em>. Exotic technology choices are risky, and it is a responsible impulse to be wary of them.</p><p>Still, the history of mainstream programming languages is essentially a story of programmers vocally and emphatically rejecting what eventually proved to be some of the most incredibly successful innovations in the history of the field. Assembly programmers largely laughed at FORTRAN, but just a few decades later, there were nevertheless very few remaining assembly programmers. First-class functions were widely derided as needlessly complicated and confusing until programmers were forced to finally take the time to learn to use them once JavaScript became a load-bearing language by historical accident, and within a decade, they became a required feature for every major programming system. Sophisticated type systems largely retain a perception of overengineered, ivory-tower elitism, but many of the programmers who hold those very opinions have enthusiastically adopted Rust, a language that features a type system so complex that idiomatic Rust code can easily put Haskell programs to shame.</p><p>Discussions of programming languages are, by and large, emotionally driven shouting matches based on anecdotes and gut feelings. Surprisingly (or perhaps not), although most working programming language theorists and compiler engineers have a far more informed perspective on this subject (and do at least tend to refrain from petty debate), disparaging remarks directed towards languages not to the speaker’s taste are hardly unheard of even at academic conferences dedicated to the subject. I want to be very clear that I do not think that is somehow indicative of some toxicity within the community—they are by and large a group of truly amazing, wonderful people, and such comments are usually delivered with tongue planted firmly in cheek. All the same, my point is simply that programming languages are an emotional, opinionated subject in a way that seems perhaps inherent to the craft, <a href="/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/">a topic I explored at some length all the way back in 2019.</a></p><p>I do earnestly believe that the field of software engineering would benefit from more openness to new ideas and willingness to experiment with new technologies. I also think it would be enormously refreshing if programmers did not nearly universally speak with an expert’s confidence on subjects they have only passingly encountered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> Nevertheless, I do not expect programmers’ tribalism to diminish anytime soon, so the pragmatist in me understands their nature imposes real limitations on what we as a field can meaningfully accomplish.</p><p>In my experience, most working programming language researchers have essentially resigned themselves to this fact, and the prevailing wisdom is to almost entirely decouple the value of the work from its impact. Even if an idea is broadly ignored by programmers today, if it’s a good enough idea, it will still make its way into some programming language in some distant tomorrow, as many ideas eventually have. Lexical closures were first applied to programming languages in 1975 but took a full thirty years to be broadly accepted. Algebraic datatypes were first introduced around the same time and have only recently found mainstream acceptance through Rust. Innovations take a long, <em>long</em> time to make the jump from research to practice in this field, and placing one’s sense of professional accomplishment in the fickle hands of mainstream programmers’ whims and preferences is a recipe for personal misery.</p><p>I have nothing but respect for this attitude, genuinely. This research community is astonishingly friendly, warm, creative, fun, and accepting, especially given the vitriol these subjects tend to elicit from programmers at large. I feel genuinely honored to have had the opportunity to personally collaborate with many of them, and the collective output of endlessly fascinating ideas and exciting projects one can find each and every year at every major PL conference is consistently inspiring. All the same, as much respect as I have for the people who work tirelessly on these problems for their own sake, I am not sure I can continue to count myself among them. I do find the subject exciting, and I do consider it rewarding in its own way, but these past few years, I have slowly been coming to terms with the fact that I don’t think I find it quite rewarding enough.</p><h2><a name="why-i-write-computer-programs"></a>Why I write computer programs</h2><p>I like writing software. It is strange how radical a statement that sometimes feels. So many programmers seem to practically despise what they do for a living, at least as you’d hear them describe it. I have never really been able to empathize with that mindset. I find programming fun, and I derive a unique joy from writing code that decisively solves the problem at hand. It doesn’t have to be a particularly interesting problem, and the solution doesn’t even have to require much more than snapping blocks together, but as long as I can take some pride and satisfaction in the craftsmanship and end result, I am usually pretty happy.</p><p>My attraction to libraries, tooling, and compilers has always stemmed from a mixture of personal motivation and a certain satisfaction from improving things for my peers. One of my favorite things about software engineering is taking a problem and solving it in such a way that it largely <em>stays solved</em>. Sure, it may need to change as requirements evolve, but the solution continues to work even after the labor is done. Other tasks in my life do not provide nearly the same sense of accomplishment. When I do a load of laundry, it provides me with clean clothes for a week or two, but ultimately, I know it won’t be long before I’m going to have to do it again. Software engineering is not like that. Working on software—even extremely mundane software—provides a pleasant variety of tasks that can be well and truly <em>completed</em>. I like that sense of progress, and I appreciate the relaxing, almost meditative process of growing and maintaining the metaphorical garden that is a collection of code.</p><p>Given all of the above, it is not altogether clear why I would struggle to take satisfaction in my work on programming languages. If anything, working on a compiler seems like the ultimate realization of my aforementioned desires applied to the very thing I, myself, spend my day doing. If there is some annoying or repetitive task, and I can extend my programming system to provide a better way of doing it, that task is something I will never again need to do. What’s more, working on a compiler that thousands of people actually use means this is not just benefiting me, it’s benefiting everyone else as well. It feels good to have that kind of impact, and to know that my effort is essentially multiplied by everyone who happens to use it.</p><p>On paper, this ought to make working on a compiler like GHC enormously satisfying. Even if I feel frustrated by mainstream attitudes towards programming languages, Haskell programmers feel differently. Surely, working on something used and appreciated by Haskellers ought to feel very rewarding. But it wasn’t really. There were parts that were nice, yes, and the problems were often interesting. But I didn’t find it <em>rewarding</em> in the way I had hoped, and I’m not sure it even compared favorably to working on bog-standard software. Why?</p><h3><a name="on-the-haskell-community"></a>On the Haskell community</h3><p>This was the hardest section of this blog post to write. It is easy to write about the things I love and the people I admire. It is perhaps even easier to write about the things I despise and the people I disdain. The Haskell community is neither. I do not <em>dislike</em> it. Several of its members are people I consider very good friends. Nonetheless, I have never managed to fall in any particular love with it, and even if that is no sin on its part, it is a somewhat sad thing to have to confess.</p><p>I want to be <em>excruciatingly</em> clear that I do not wish to mount any indictment of the Haskell community or any of the people in it. Many people seem to have a preconception of Haskellers as mean and elitist, and while I do get the sense there have been some historically bad actors that contributed to that perception, my general impression is that those people have been thoroughly expunged. I have never been the target of any memorably rude conduct, and I’ve definitely never felt targeted by any prejudice or discrimination, and if anything I have always felt universally warmly welcomed at every meetup or conference I have attended. I cannot speak for anyone but myself, and my experiences are necessarily limited. I cannot hope to claim with any certainty that no abuse has ever occurred or that it does not continue to occur within any Haskell’s myriad social spaces, most of which I have never participated in. Still, I am happy to be able to say that I have never personally experienced any real unfriendliness, and that is certainly not a factor in my dispassion for the language’s community. If anything, I feel I must extend a very genuine <em>thank you</em> for all the lovely support I have received from Haskellers over the years, and it is that support and enthusiasm that so often kept me motivated for as long as I was.</p><p>This is all simply to say that I do not think it is the <em>fault</em> of anyone in particular for my lack of affinity for the Haskell community. Rather, I think perhaps it is just not a culture that I have ever especially related to. That probably does not even say very much, as I’m not sure there are many communities I <em>could</em> say that about. That says much more about me than it does about Haskell or Haskellers.</p><p>It is admittedly tempting to say that perhaps I find these communities alienating in part because they lack people I identify with and relate to. After all, it is simply true that the Haskell community is <em>overwhelmingly</em> male, which really has felt lonely at times. It’s also true that the Haskell community tends to attract a lot of people who are mainly interested in Haskell because they find it intellectually interesting, or because they like category theory and enjoy libraries like <code>lens</code> that have explored those ideas, while I am really only interested in Haskell insofar as it is a practical vehicle for writing useful software. Nevertheless, there <em>are</em> other women who write Haskell and there <em>are</em> other people who care very much about writing useful software in Haskell, and that doesn’t seem to have radically altered my relationship to the community, so I think it would be disingenuous of me to claim that either of those flaws are substantially to blame.</p><p>If there is one substantive frustration I can express with Haskell, it is where the money comes from. For as long as I have been involved with it, Haskell has predominantly been used and funded by a combination of fintech and cryptocurrency. I don’t really have anything against fintech, though I certainly don’t find it especially exciting, but I do have something against crypto, and those who followed me on Twitter before its untimely demise know I have always been pretty open about that fact. My thoughts on the matter are really not something I want to elaborate on here, but suffice to say it is not especially motivating to work on a language if all of its users are applying it in ways I feel essentially indifferent about at best and actively hostile to at worst. This is obviously not something I have any easy answers to, but it is probably the most significant, concrete factor in me losing my interest in working on GHC, so if you mourn me leaving, at least I can give you one semi-actionable reason.</p><p>Make no mistake: GHC is a truly incredible project that often still feels like a compiler from the future, and having the opportunity to work on it as a part of my job was about as technologically stimulating as I could possibly ask for. Even so, during my time at Tweag, I often found myself reflecting on the fact that the <em>users</em> I was working for were, ultimately, Haskell programmers, a class I’m not sure even really contains <em>me</em>. I have often reflected on the fact that, although I have many personal projects, I have <em>never</em> chosen Haskell for any of them. Not once. Whenever I need to write a little code to accomplish some task, it’s always Racket. This blog is in Racket. My personal shell utilities are in Racket. When I want to scrape a website or wrap some library, I do it with Racket. Haskell is a language I have always exclusively written professionally, and although I do very earnestly love many things about it, it is not so precious to me that I feel motivated to contribute to it out of passion alone. If there were more <em>users</em> of Haskell doing inspiring, exciting things with the tools I was working on, I might feel substantially more driven to indirectly contribute to those causes. But Haskell is a niche language used by a select few, and it is hard for me to spend so much of my life working on a technological marvel that practically nobody will ever benefit from.</p><p>As a final note on this subject: I do believe very earnestly that the functional programming and programming languages communities would benefit enormously from more demographic diversity. Even compared to software engineering as a whole, the gender ratio is, frankly, almost unbelievably grim. I have always been drawn to interests and hobbies that tend to skew male, and that has never really bothered me, so if <em>I</em> have found myself feeling alienated by the conspicuous absence of other women—to the point that, in the past, I have sometimes considered giving up on Haskell primarily for that reason—I don’t think it is outrageous to say things are pretty dire. A wider set of perspectives could, in theory, inject some refreshing creativity into the dreary ecosystem that is industrial Haskell. Of course, I will admit that I have absolutely no idea how that end could possibly be achieved, and it is a subject far broader and more complicated than I think belongs in this blog post. For now, I will leave things at that, but I hope my voice is sufficiently well regarded that some readers may take my comments to heart.</p><h2><a name="reflections-on-a-decade-s-work"></a>Reflections on a decade’s work</h2><p>Before writing this blog post, I essentially pitched it to a group of friends as “a sort of apology”. At times I certainly feel burdened by the weight of all the endeavors I’ve left unfinished, all the work I never carried long enough to bear fruit. Even so, as I write these words, I find myself reflecting on the things I feel I <em>did</em> manage to contribute these past ten years, and it strikes me that the trail I’ve left behind is perhaps not so dismal, after all.</p><p>It is comforting to look back on a decade’s accumulations and realize there is enough to take some real pride in. I am proud of my contributions to Racket and Haskell, and of my numerous libraries in each of them. I am proud of my experiments, among them <a href="https://github.com/lexi-lambda/hackett">Hackett</a> and <a href="https://github.com/lexi-lambda/eff">eff</a>, and the excellent work from others they have inspired. I am proud of this very blog, from the <a href="/blog/2019/11/05/parse-don-t-validate/">most impactful</a> things I’ve written to the <a href="/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/">far more niche</a>. I am proud of my <a href="https://langdev.stackexchange.com/users/861/alexis-king?tab=answers&amp;sort=newest">contributions to Programming Languages Stack Exchange</a>, a site I most certainly intend to remain a moderator of and will undoubtedly continue to contribute to. And I am proud of the <a href="https://www.youtube.com/watch?v=0jI-AlWEwYI">numerous talks</a> I have <a href="https://www.youtube.com/watch?v=TE48LsgVlIU">presented over the years</a>, all of them labors of love.</p><p>I have not always accomplished all the things I set out to achieve. Some have, for some reason or another, gotten the better of me. Many of them, from my <a href="https://github.com/lexi-lambda/blackboard">math typesetting system</a> to my <a href="https://github.com/ghc-proposals/ghc-proposals/pull/303">improvements to GHC’s arrow notation</a>, are things I hope to one day push over the finish line. But for now it is clear my heart is no longer really in them, so I think it is time for me to let them go. Perhaps others will find inspiration in them. Perhaps you will succeed where I did not.</p><p>It is a little sad to think about moving on from such a substantial chapter of my life. To date, I have spent almost my entire career in this professional niche, and I suspect I will never fully leave it. It has been a pleasure to work with so many bright, lovely, inspiring people, and perhaps one day I will find myself eager to return once more. But for now, my interests are elsewhere, and despite the bittersweet feelings, there is also a very real excitement in the tantalizing opportunity to do something new.</p><h2><a name="what-next"></a>What next?</h2><p>It is a little amusing to think that what I find myself most drawn to after a decade of pursuing ever more exotic projects is something entirely mundane: I want to write utterly ordinary software. I want to work on a piece of software used by people to accomplish a task, and I want to take pride and satisfaction in doing it well.</p><p>I do not yet really have any idea what that might look like. It has been over six years since I last worked on anything that really feels like it could possibly fit that description, and even then, it was only one part of my job. Is it so strange that after spending fifteen years of my life trying everything I could to get away from spending all day wrangling a big ball of mud in a Java IDE, that experience sounds pretty cozy to return to?</p><p>In late 2022, I decided on a whim to reverse engineer and resurrect a 2008 Java browser game called <a href="https://github.com/lexi-lambda/shattered-plans">Shattered Plans</a>. While doing so, I had the opportunity to spend an awful lot of time in IntelliJ, and after years of acclimatizing to writing code in fanciful languages with nothing more than a plain text editor, a terminal emulator, and a dream, the experience was almost viscerally thrilling. They say the grass is always greener, but even if that is true, I think I’d like to find it out for myself.</p><p>More to the point, my life is just very different now from how it was for most of my twenties. I was single, and it was easy to justify spending almost all of my time thinking about code, whether on the clock or not. It was hardly unheard of for me to spend twelve or even sixteen hours at my desk, just tinkering on something that had captured my attention. It was fun, and I’m glad I did it, but I am also very ready to work a simple, regular job I don’t feel the need to permanently devote a large portion of my soul to. It isn’t very romantic, but then, I have other things for that.</p><p>If you think you have a position that meets those (admittedly extremely broad) criteria, and you think you might like to hire me, by all means: <a href="mailto:lexi.lambda@gmail.com">send me an email</a>. The worst I can do is not reply. And who knows? With the burden of my own expectations behind me, perhaps I’ll even blow the dust off this old blog of mine and start writing things again. As with my job, whatever I might choose to write next I cannot say. Either way, it’s a little exciting to be able to treat myself to a blank slate.</p><p>I could probably spend twenty thousand words writing in circles about all my thoughts and feelings on this subject. However, to be honest, I don’t especially want to. As this blog post has hopefully communicated, I would like to be done, and so this will have to be enough. Allow me to personally thank all of you who have indulged me by reading my disorganized ramblings to the end.</p><p>It’s been a long time. Here’s to another ten years.</p><ol class="footnotes"><li id="footnote-1"><p>Readers may find it clarifying to note that a great deal of my early programming experience was writing (then era-appropriate) Java 6. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I have often wondered if other engineering disciplines have anything to rival the collective overconfidence of the average Hacker News commenter. This is an earnest curiosity, not merely a rhetorical flourish; I do sometimes wonder what it is about my field of choice that engenders such disregard for the value of expertise. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/philosophy.atom.xml b/feeds/philosophy.atom.xml new file mode 100644 index 0000000..9b53525 --- /dev/null +++ b/feeds/philosophy.atom.xml @@ -0,0 +1 @@ +Posts tagged ‘philosophy’ | Alexis King’s Blog2019-10-19T00:00:00ZEmpathy and subjective experience in programming languages2019-10-19T00:00:00Z2019-10-19T00:00:00ZAlexis King<article><p>A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.</p><p>Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?</p><p>I think about that question a lot.</p><h2><a name="2015-called-and-they-want-their-dress-back"></a>2015 called, and they want their dress back</h2><p>Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.</p><p>However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into <em>Star Wars</em>. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.</p><p>Take 2015’s phenomenon of “<a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did <em>this</em>—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.</p><h3><a name="when-something-objective-isn-t"></a>When something objective isn’t</h3><p>Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.</p><p>The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.</p><p>In the case of the dress, whether you <a href="https://en.wikipedia.org/wiki/Yanny_or_Laurel">heard “yanny” or “laurel,”</a> or whether you believe the <em>Sonic</em> games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?</p><p>I think the answer is absolutely, unequivocally <em>yes</em>.</p><h2><a name="subjectivity-in-programming-and-in-programming-languages-specifically"></a>Subjectivity in programming, and in programming languages specifically</h2><p>Quick question: which is better, functional or imperative programming?</p><p>My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?</p><p>Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?</p><p>I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> but I did happen to find a link to <a href="https://news.ycombinator.com/item?id=21282647">a recent discussion</a> on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?</p><p>In a <a href="https://news.ycombinator.com/item?id=21284383">branch of the discussion</a>, one user writes:</p><blockquote><blockquote><p>Haskell is great for business and great in production</p></blockquote><p>I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.</p><p>I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.</p></blockquote><p>The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:</p><blockquote><p>I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.</p><p>[…]</p><p>From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.</p></blockquote><p>Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:</p><blockquote><p>Haskell gives one plenty of rope to hang himself on complexity.</p><p>So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.</p></blockquote><p>Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:</p><blockquote><p>Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.</p><p>[…]</p><p>The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.</p><p>Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.</p></blockquote><p>That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?</p><h3><a name="the-unsatisfying-subjective-reality-of-programming-languages"></a>The unsatisfying subjective reality of programming languages</h3><p>You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?</p><p><em><strong>No!</strong></em></p><p>These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, <em>why?</em> Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.</p><p>While <a href="https://news.ycombinator.com/item?id=21284317">one commenter</a> in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” <a href="https://news.ycombinator.com/item?id=21284540">another</a> says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.</p><p>Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t <em>get it.</em> There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.</p><h3><a name="empathy-and-how-bad-results-come-from-good-intentions"></a>Empathy, and how bad results come from good intentions</h3><p>I’ll admit that these kinds of discussions aren’t <em>always</em> fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.</p><p>Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.</p><p>Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:</p><blockquote><p>Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.</p></blockquote><p>I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?</p><p>If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.</p><p>Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.</p><p>To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:</p><blockquote><p>It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.</p><p>Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.</p></blockquote><p>When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective <strong>without</strong> invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about <em>your own</em> experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.</p><p>It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.</p><p>Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be <em>happier.</em></p><ol class="footnotes"><li id="footnote-1"><p>This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is <em>blah</em>,” but you deserve better. So you, uh, get a <em>meta</em> snarky footnote instead. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Which, to be entirely fair, may well be as subjective as anything else in this blog post. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/philosophy.rss.xml b/feeds/philosophy.rss.xml new file mode 100644 index 0000000..f2b5c82 --- /dev/null +++ b/feeds/philosophy.rss.xml @@ -0,0 +1 @@ +Posts tagged ‘philosophy’ | Alexis King’s BlogPosts tagged ‘philosophy’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/philosophy.html19 Oct 201919 Oct 201960Empathy and subjective experience in programming languageshttps://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/https://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/19 Oct 2019<article><p>A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.</p><p>Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?</p><p>I think about that question a lot.</p><h2><a name="2015-called-and-they-want-their-dress-back"></a>2015 called, and they want their dress back</h2><p>Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.</p><p>However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into <em>Star Wars</em>. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.</p><p>Take 2015’s phenomenon of “<a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did <em>this</em>—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.</p><h3><a name="when-something-objective-isn-t"></a>When something objective isn’t</h3><p>Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.</p><p>The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.</p><p>In the case of the dress, whether you <a href="https://en.wikipedia.org/wiki/Yanny_or_Laurel">heard “yanny” or “laurel,”</a> or whether you believe the <em>Sonic</em> games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?</p><p>I think the answer is absolutely, unequivocally <em>yes</em>.</p><h2><a name="subjectivity-in-programming-and-in-programming-languages-specifically"></a>Subjectivity in programming, and in programming languages specifically</h2><p>Quick question: which is better, functional or imperative programming?</p><p>My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?</p><p>Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?</p><p>I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> but I did happen to find a link to <a href="https://news.ycombinator.com/item?id=21282647">a recent discussion</a> on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?</p><p>In a <a href="https://news.ycombinator.com/item?id=21284383">branch of the discussion</a>, one user writes:</p><blockquote><blockquote><p>Haskell is great for business and great in production</p></blockquote><p>I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.</p><p>I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.</p></blockquote><p>The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:</p><blockquote><p>I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.</p><p>[…]</p><p>From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.</p></blockquote><p>Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:</p><blockquote><p>Haskell gives one plenty of rope to hang himself on complexity.</p><p>So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.</p></blockquote><p>Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:</p><blockquote><p>Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.</p><p>[…]</p><p>The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.</p><p>Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.</p></blockquote><p>That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?</p><h3><a name="the-unsatisfying-subjective-reality-of-programming-languages"></a>The unsatisfying subjective reality of programming languages</h3><p>You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?</p><p><em><strong>No!</strong></em></p><p>These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, <em>why?</em> Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.</p><p>While <a href="https://news.ycombinator.com/item?id=21284317">one commenter</a> in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” <a href="https://news.ycombinator.com/item?id=21284540">another</a> says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.</p><p>Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t <em>get it.</em> There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.</p><h3><a name="empathy-and-how-bad-results-come-from-good-intentions"></a>Empathy, and how bad results come from good intentions</h3><p>I’ll admit that these kinds of discussions aren’t <em>always</em> fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.</p><p>Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.</p><p>Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:</p><blockquote><p>Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.</p></blockquote><p>I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?</p><p>If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.</p><p>Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.</p><p>To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:</p><blockquote><p>It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.</p><p>Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.</p></blockquote><p>When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective <strong>without</strong> invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about <em>your own</em> experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.</p><p>It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.</p><p>Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be <em>happier.</em></p><ol class="footnotes"><li id="footnote-1"><p>This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is <em>blah</em>,” but you deserve better. So you, uh, get a <em>meta</em> snarky footnote instead. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Which, to be entirely fair, may well be as subjective as anything else in this blog post. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/programming-languages.atom.xml b/feeds/programming-languages.atom.xml new file mode 100644 index 0000000..2b5f4e5 --- /dev/null +++ b/feeds/programming-languages.atom.xml @@ -0,0 +1,254 @@ +Posts tagged ‘programming languages’ | Alexis King’s Blog2025-05-29T00:00:00ZA break from programming languages2025-05-29T00:00:00Z2025-05-29T00:00:00ZAlexis King<article><p>This is a blog post I have been considering writing for a long time.</p><p>People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.</p><p>My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.</p><p>This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for <em>me</em>, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why <em>not</em>—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.</p><h2><a name="whence-programming-languages"></a>Whence programming languages</h2><p>I have had a personal fascination with programming languages for as long as I have been using them. I made my first serious forays into programming in the fifth grade, writing (very simple and mostly very bad) Flash games in ActionScript 3. I wrote my first programming language interpreter in my sophomore year of high school, a dynamically-typed, object-oriented Lisp with prototypal inheritance. The interpreter was written in C, and despite it being my first serious endeavor with the language, I used preprocessor macros to implement a form of exceptions in terms of <code>setjmp.h</code>. It is an amusing project in retrospect, given what my design sensibilities would eventually turn out to be, but it at least provides a little insight into how much the subject was already on my mind in my teenage years.</p><p>I have always deeply enjoyed programming. That is no less true now than it was fifteen years ago. As someone with (then untreated) ADHD, the automation of repetitive tasks has always held immediate appeal, but it did not take long to discover I enjoy programming for its own sake. It is inherently delightful to me to direct an infinitely reconfigurable machine towards any end I desire, given only a little thought and some time at a keyboard. Program <em>design</em>, even in the large, was something that immediately appealed to me. It is often as much a challenge of organizing one’s thoughts as it is a matter of translating them to a form the computer can understand, and the former is a challenge I have always found rewarding.</p><p>Even so, as every programmer inevitably discovers, the gap between one’s thoughts and executable code is never so narrow as we would like. I quickly discovered that a distressingly large amount of my time was being spent on the exactly the sort of repetitive task I had hoped to avoid, mechanically typing out reams of boilerplate in order to carry out what seemed to be incredibly simple tasks.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> It felt strange that we, as programmers, could be one of the lucky disciplines equipped with the means to produce our own tools, yet the tools we work with are so frequently unsatisfactory.</p><p>Programming is an astonishingly young discipline. FORTRAN, arguably the first recognizable programming language, is less than seventy years old, and the first stored-program computers are only a decade older. Programmers have been trying to bridge the gap between our conceptualizations of programs and the (often laborious) act of programming for as long as computing has existed. As John Backus said in 1979,</p><blockquote><p>Much of my work has come from being lazy. I didn’t like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs.</p></blockquote><p>In my experience, this sentiment perfectly captures the strange relationship many programming language enthusiasts have with our craft. We enjoy programming so much that we are willing to spend enormous time, thought, and effort working on programming systems precisely to free ourselves from the tortuous burden of writing programs.</p><p>One possible means of reconciling this apparent contradiction would be to hypothesize that programmers like me or like Backus enjoy the <em>results</em> of our programs but not the process of writing them. We want to get things done, and we view the computer as a useful but necessary evil that should get out of the way as much and as expediently as possible. However, although I am sure there are many programmers who do match that description, I do not count myself among them, and I would likewise find it extremely difficult to believe that Backus—a man who dedicated his life to designing ever more sophisticated means of expressing computer programs—was someone fundamentally uninterested in programming.</p><p>On the contrary, I see a great deal of work on programming languages as a struggle—often in vain—by those of us who love programming so much that we wish to free it of its unfortunate imperfections. We strive to remove the impediments and obstacles we face when translating the beautiful, platonic programs we hold in our heads into the comparatively cold, alien language of silicon calculating machines. There is a widespread mythology that programming languages are primarily abstractions over machine code, but this perspective has been misguided for almost as long as programming languages have existed. Programming languages are instruments of thought, frameworks for precisely specifying an executable idea, and they are designed almost entirely for <em>humans</em>, with computers often providing little more than a loose set of inconvenient restrictions.</p><h3><a name="to-compute-is-human-to-program-divine"></a>To compute is human, to program divine</h3><p>The earliest computers were developed with a singular purpose in mind: to compute. A program was, essentially, a sequence of instructions containing the computations to be performed. This conceptualization of a computer program as essentially analogous to a cooking recipe or assembly manual has remained curiously persistent, even to the present day.</p><p>Almost no modern programs of any complexity look anything like a cooking recipe. Even programs written in the most imperative programming languages under the sun are complex webs of logic specifying dataflow, access policy, and interaction patterns between semi-autonomous, reactive subsystems, many of which come equipped with their own living, beating hearts. At the micro scale, it is true that programs specify behavior as a series of steps to be performed, but even this is a fiction maintained by modern compilers for humans’ convenience. Through a compiler’s eyes, the <em>real</em> programs are whatever is left after the comforting fiction is boiled away to obtain a homogenous pile of SSA and dataflow graphs to be haphazardly reassembled into a patchwork of basic blocks in a language most programmers would not even begin to understand.</p><p>Indeed, in stark contrast to the mental model of the programmer as a sort of silicon whisperer, I believe most practicing software engineers are primarily maintaining vast behavioral specifications whose defining sense of rigor comes not from computers <em>per se</em> but from the consistency we demand from their results. In other words, we are trained to be fluent speakers of languages that allow us to articulate a solution to a problem with a level of precision that ensures every legitimate interpretation is and will always be to our satisfaction. Crucially, these specifications do not <em>fully</em> specify how the task is to be carried out. Optimizing compilers are free to make their own choices with whatever leeway we permit them. HTML/CSS user agents may interpret our instructions in whatever way preserves whatever web standards happen to require. Even RDBMS query planners, a foundational technology largely unchanged for the past several decades, are essentially expert systems designed to decide on the fly how best to execute searches and retrievals under ever-changing conditions.</p><p>This is not to say that the task of programming is fully removed from the machine that ultimately executes our instructions, nor is it even really a suggestion it ought to be. Performance and debugging mandate a certain familiarity with the underlying system, even if the full complexities of its true inner workings remain carefully hidden from even its expert users. My point is simply that programs are not rote algorithms, and the act of programming is not primarily characterized by algorithmic thinking. Rather, programming is fundamentally a game of domain modeling and system design, with bits of algorithmic logic inserted here and there to glue the whole thing together.</p><p>My interest in pushing the frontier of programming language design has always been and continues to be an interest in making the task of writing programs feel closer to the task of designing them. In an ideal world, I would have a language expressive enough to more or less directly translate the computer programs I have in my head (which, for me, more often than not take <em>visual</em> form) into a digitized structure a computer may faithfully execute. It is that task I found myself fixated on at the tender age of fifteen, and it is one I would remain fixated on for the better part of the following decade.</p><h2><a name="ten-years-of-programming-languages"></a>Ten years of programming languages</h2><p>I turned twenty eight this year. Almost exactly ten years ago, I started my first professional software engineering job. It was a fairly mundane position writing Ruby on Rails and JavaScript, but even then, I spent a great deal of free time pursuing the programming language hobby projects that truly caught my fancy. My first professional experience working with Haskell would come less than a year later, experience that would soon inspire my hobby research project to implement a Haskell-style type system within the Racket macro system. The majority of my career to date has followed more or less directly from that formative time.</p><p>After a decade, it seems fair to reflect a little on what I have learned, what I managed to achieve, and maybe more significantly, what I did <em>not</em>. Something one learns very quickly upon beginning any serious work on programming languages is that they are startlingly, unforgivably <em>hard</em>. A vast array of dizzyingly smart people have been working on the problem of program specification for seventy years; there are not many pieces of low hanging fruit left on the tree, and any new gains tend to be very hard won.</p><p>Still, even if programming language design is challenging, it would be some consolation if we were able to slowly yet steadily advance the state of the art, gradually picking away at the gap between the tools we have now and the ultimate programming language of the future. Sadly, even that pragmatic vision does not survive contact with reality. In practice, the challenge of programming language design is not one of expanding a well-defined frontier, it is grappling with a neverending list of fundamental <em>tradeoffs</em> between mutually incompatible features.</p><p>Subtyping is tantalizingly useful but makes complete type inference incredibly difficult (and in general, provably impossible). Structural typing drastically reduces the burden of assigning a <em>name</em> to every uninteresting intermediate form but more or less precludes taking advantage of Haskell-style typeclasses or Rust-style traits. Dynamic dispatch substantially assists decoupling of software components but can come with a significant performance cost without a JIT. Just-in-time compilation can use runtime information to optimize code in ways that permit more flexible coding patterns, but JIT performance can be unpredictable, and the overhead of an optimizing JIT is substantial. Sophisticated metaprogramming systems can radically cut down on boilerplate and improve program concision and even readability, but they can have substantial runtime or compile-time overhead and tend to thwart automated tooling. The list goes on and on.</p><p>The essential, most stubborn problems in programming languages come from unavoidable tensions between conflicting desires and requirements. We want loosely coupled software components that can be easily reused, but we also want the performance benefits of tight coupling and specialization. We want flexible programming languages that do not impose upon our freedom of expression, but we also want the benefits of static program analysis and powerful safety guarantees. We want sophisticated type systems that allow specifying ever more complex invariants, but we also want readable type signatures that won’t regularly end up longer than the code itself.</p><p>We cannot build the One True Programming Language because we cannot have everything at once, and we cannot hope to universally decide which tradeoffs are the right ones to make. This naturally makes the thought of a system constructed from a tapestry of many different programming languages attractive, affording the programmer the opportunity to select the tool best suited to the task at hand. Unfortunately, that, too, comes with (often brutal) tradeoffs of its own: the impedance mismatch at language boundaries, the vastly more complex tooling required for a polyglot system, and the increased knowledge burden of maintaining a system built in so many different ways.</p><p>Ten years of working on and thinking about programming languages has forced me to come to terms with a humbling truth: I do not know how to build a better programming language. I am personally extremely sympathetic to the idea that the future of programming probably involves more domain-specific languages, and I think the industry as a whole has been (very slowly) trending in that direction for quite some time. But even I acknowledge that the question of how precisely that ought to be done is extraordinarily complicated, and there are no easy answers here.</p><p>Even so, it would be far too dismal of me to suggest that the problem is hopeless, nor do I believe the work does not matter. Even if the problem has proven more difficult than teenage me may have hoped, the work <em>is</em> satisfying when it bears fruit. Unfortunately, even after toiling for years overcoming daunting problems, every working programming language enthusiast is forced to confront a much more frustrating enemy: <em>programmers</em>.</p><h3><a name="the-reactionary-conservatism-of-the-median-programmer"></a>The reactionary conservatism of the median programmer</h3><p>It should come as no surprise that programming language design is largely a social problem. Choice of programming language tends to be driven by strong network effects, and programmers tend to prefer languages that resemble what they already know. Programmers’ general lack of curiosity and outright hostility to learning new things has always been a personal frustration of mine, but I ultimately do understand much of where it comes from: as engineers, an overwhelming part of our job description is managing <em>risk</em>. Exotic technology choices are risky, and it is a responsible impulse to be wary of them.</p><p>Still, the history of mainstream programming languages is essentially a story of programmers vocally and emphatically rejecting what eventually proved to be some of the most incredibly successful innovations in the history of the field. Assembly programmers largely laughed at FORTRAN, but just a few decades later, there were nevertheless very few remaining assembly programmers. First-class functions were widely derided as needlessly complicated and confusing until programmers were forced to finally take the time to learn to use them once JavaScript became a load-bearing language by historical accident, and within a decade, they became a required feature for every major programming system. Sophisticated type systems largely retain a perception of overengineered, ivory-tower elitism, but many of the programmers who hold those very opinions have enthusiastically adopted Rust, a language that features a type system so complex that idiomatic Rust code can easily put Haskell programs to shame.</p><p>Discussions of programming languages are, by and large, emotionally driven shouting matches based on anecdotes and gut feelings. Surprisingly (or perhaps not), although most working programming language theorists and compiler engineers have a far more informed perspective on this subject (and do at least tend to refrain from petty debate), disparaging remarks directed towards languages not to the speaker’s taste are hardly unheard of even at academic conferences dedicated to the subject. I want to be very clear that I do not think that is somehow indicative of some toxicity within the community—they are by and large a group of truly amazing, wonderful people, and such comments are usually delivered with tongue planted firmly in cheek. All the same, my point is simply that programming languages are an emotional, opinionated subject in a way that seems perhaps inherent to the craft, <a href="/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/">a topic I explored at some length all the way back in 2019.</a></p><p>I do earnestly believe that the field of software engineering would benefit from more openness to new ideas and willingness to experiment with new technologies. I also think it would be enormously refreshing if programmers did not nearly universally speak with an expert’s confidence on subjects they have only passingly encountered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> Nevertheless, I do not expect programmers’ tribalism to diminish anytime soon, so the pragmatist in me understands their nature imposes real limitations on what we as a field can meaningfully accomplish.</p><p>In my experience, most working programming language researchers have essentially resigned themselves to this fact, and the prevailing wisdom is to almost entirely decouple the value of the work from its impact. Even if an idea is broadly ignored by programmers today, if it’s a good enough idea, it will still make its way into some programming language in some distant tomorrow, as many ideas eventually have. Lexical closures were first applied to programming languages in 1975 but took a full thirty years to be broadly accepted. Algebraic datatypes were first introduced around the same time and have only recently found mainstream acceptance through Rust. Innovations take a long, <em>long</em> time to make the jump from research to practice in this field, and placing one’s sense of professional accomplishment in the fickle hands of mainstream programmers’ whims and preferences is a recipe for personal misery.</p><p>I have nothing but respect for this attitude, genuinely. This research community is astonishingly friendly, warm, creative, fun, and accepting, especially given the vitriol these subjects tend to elicit from programmers at large. I feel genuinely honored to have had the opportunity to personally collaborate with many of them, and the collective output of endlessly fascinating ideas and exciting projects one can find each and every year at every major PL conference is consistently inspiring. All the same, as much respect as I have for the people who work tirelessly on these problems for their own sake, I am not sure I can continue to count myself among them. I do find the subject exciting, and I do consider it rewarding in its own way, but these past few years, I have slowly been coming to terms with the fact that I don’t think I find it quite rewarding enough.</p><h2><a name="why-i-write-computer-programs"></a>Why I write computer programs</h2><p>I like writing software. It is strange how radical a statement that sometimes feels. So many programmers seem to practically despise what they do for a living, at least as you’d hear them describe it. I have never really been able to empathize with that mindset. I find programming fun, and I derive a unique joy from writing code that decisively solves the problem at hand. It doesn’t have to be a particularly interesting problem, and the solution doesn’t even have to require much more than snapping blocks together, but as long as I can take some pride and satisfaction in the craftsmanship and end result, I am usually pretty happy.</p><p>My attraction to libraries, tooling, and compilers has always stemmed from a mixture of personal motivation and a certain satisfaction from improving things for my peers. One of my favorite things about software engineering is taking a problem and solving it in such a way that it largely <em>stays solved</em>. Sure, it may need to change as requirements evolve, but the solution continues to work even after the labor is done. Other tasks in my life do not provide nearly the same sense of accomplishment. When I do a load of laundry, it provides me with clean clothes for a week or two, but ultimately, I know it won’t be long before I’m going to have to do it again. Software engineering is not like that. Working on software—even extremely mundane software—provides a pleasant variety of tasks that can be well and truly <em>completed</em>. I like that sense of progress, and I appreciate the relaxing, almost meditative process of growing and maintaining the metaphorical garden that is a collection of code.</p><p>Given all of the above, it is not altogether clear why I would struggle to take satisfaction in my work on programming languages. If anything, working on a compiler seems like the ultimate realization of my aforementioned desires applied to the very thing I, myself, spend my day doing. If there is some annoying or repetitive task, and I can extend my programming system to provide a better way of doing it, that task is something I will never again need to do. What’s more, working on a compiler that thousands of people actually use means this is not just benefiting me, it’s benefiting everyone else as well. It feels good to have that kind of impact, and to know that my effort is essentially multiplied by everyone who happens to use it.</p><p>On paper, this ought to make working on a compiler like GHC enormously satisfying. Even if I feel frustrated by mainstream attitudes towards programming languages, Haskell programmers feel differently. Surely, working on something used and appreciated by Haskellers ought to feel very rewarding. But it wasn’t really. There were parts that were nice, yes, and the problems were often interesting. But I didn’t find it <em>rewarding</em> in the way I had hoped, and I’m not sure it even compared favorably to working on bog-standard software. Why?</p><h3><a name="on-the-haskell-community"></a>On the Haskell community</h3><p>This was the hardest section of this blog post to write. It is easy to write about the things I love and the people I admire. It is perhaps even easier to write about the things I despise and the people I disdain. The Haskell community is neither. I do not <em>dislike</em> it. Several of its members are people I consider very good friends. Nonetheless, I have never managed to fall in any particular love with it, and even if that is no sin on its part, it is a somewhat sad thing to have to confess.</p><p>I want to be <em>excruciatingly</em> clear that I do not wish to mount any indictment of the Haskell community or any of the people in it. Many people seem to have a preconception of Haskellers as mean and elitist, and while I do get the sense there have been some historically bad actors that contributed to that perception, my general impression is that those people have been thoroughly expunged. I have never been the target of any memorably rude conduct, and I’ve definitely never felt targeted by any prejudice or discrimination, and if anything I have always felt universally warmly welcomed at every meetup or conference I have attended. I cannot speak for anyone but myself, and my experiences are necessarily limited. I cannot hope to claim with any certainty that no abuse has ever occurred or that it does not continue to occur within any Haskell’s myriad social spaces, most of which I have never participated in. Still, I am happy to be able to say that I have never personally experienced any real unfriendliness, and that is certainly not a factor in my dispassion for the language’s community. If anything, I feel I must extend a very genuine <em>thank you</em> for all the lovely support I have received from Haskellers over the years, and it is that support and enthusiasm that so often kept me motivated for as long as I was.</p><p>This is all simply to say that I do not think it is the <em>fault</em> of anyone in particular for my lack of affinity for the Haskell community. Rather, I think perhaps it is just not a culture that I have ever especially related to. That probably does not even say very much, as I’m not sure there are many communities I <em>could</em> say that about. That says much more about me than it does about Haskell or Haskellers.</p><p>It is admittedly tempting to say that perhaps I find these communities alienating in part because they lack people I identify with and relate to. After all, it is simply true that the Haskell community is <em>overwhelmingly</em> male, which really has felt lonely at times. It’s also true that the Haskell community tends to attract a lot of people who are mainly interested in Haskell because they find it intellectually interesting, or because they like category theory and enjoy libraries like <code>lens</code> that have explored those ideas, while I am really only interested in Haskell insofar as it is a practical vehicle for writing useful software. Nevertheless, there <em>are</em> other women who write Haskell and there <em>are</em> other people who care very much about writing useful software in Haskell, and that doesn’t seem to have radically altered my relationship to the community, so I think it would be disingenuous of me to claim that either of those flaws are substantially to blame.</p><p>If there is one substantive frustration I can express with Haskell, it is where the money comes from. For as long as I have been involved with it, Haskell has predominantly been used and funded by a combination of fintech and cryptocurrency. I don’t really have anything against fintech, though I certainly don’t find it especially exciting, but I do have something against crypto, and those who followed me on Twitter before its untimely demise know I have always been pretty open about that fact. My thoughts on the matter are really not something I want to elaborate on here, but suffice to say it is not especially motivating to work on a language if all of its users are applying it in ways I feel essentially indifferent about at best and actively hostile to at worst. This is obviously not something I have any easy answers to, but it is probably the most significant, concrete factor in me losing my interest in working on GHC, so if you mourn me leaving, at least I can give you one semi-actionable reason.</p><p>Make no mistake: GHC is a truly incredible project that often still feels like a compiler from the future, and having the opportunity to work on it as a part of my job was about as technologically stimulating as I could possibly ask for. Even so, during my time at Tweag, I often found myself reflecting on the fact that the <em>users</em> I was working for were, ultimately, Haskell programmers, a class I’m not sure even really contains <em>me</em>. I have often reflected on the fact that, although I have many personal projects, I have <em>never</em> chosen Haskell for any of them. Not once. Whenever I need to write a little code to accomplish some task, it’s always Racket. This blog is in Racket. My personal shell utilities are in Racket. When I want to scrape a website or wrap some library, I do it with Racket. Haskell is a language I have always exclusively written professionally, and although I do very earnestly love many things about it, it is not so precious to me that I feel motivated to contribute to it out of passion alone. If there were more <em>users</em> of Haskell doing inspiring, exciting things with the tools I was working on, I might feel substantially more driven to indirectly contribute to those causes. But Haskell is a niche language used by a select few, and it is hard for me to spend so much of my life working on a technological marvel that practically nobody will ever benefit from.</p><p>As a final note on this subject: I do believe very earnestly that the functional programming and programming languages communities would benefit enormously from more demographic diversity. Even compared to software engineering as a whole, the gender ratio is, frankly, almost unbelievably grim. I have always been drawn to interests and hobbies that tend to skew male, and that has never really bothered me, so if <em>I</em> have found myself feeling alienated by the conspicuous absence of other women—to the point that, in the past, I have sometimes considered giving up on Haskell primarily for that reason—I don’t think it is outrageous to say things are pretty dire. A wider set of perspectives could, in theory, inject some refreshing creativity into the dreary ecosystem that is industrial Haskell. Of course, I will admit that I have absolutely no idea how that end could possibly be achieved, and it is a subject far broader and more complicated than I think belongs in this blog post. For now, I will leave things at that, but I hope my voice is sufficiently well regarded that some readers may take my comments to heart.</p><h2><a name="reflections-on-a-decade-s-work"></a>Reflections on a decade’s work</h2><p>Before writing this blog post, I essentially pitched it to a group of friends as “a sort of apology”. At times I certainly feel burdened by the weight of all the endeavors I’ve left unfinished, all the work I never carried long enough to bear fruit. Even so, as I write these words, I find myself reflecting on the things I feel I <em>did</em> manage to contribute these past ten years, and it strikes me that the trail I’ve left behind is perhaps not so dismal, after all.</p><p>It is comforting to look back on a decade’s accumulations and realize there is enough to take some real pride in. I am proud of my contributions to Racket and Haskell, and of my numerous libraries in each of them. I am proud of my experiments, among them <a href="https://github.com/lexi-lambda/hackett">Hackett</a> and <a href="https://github.com/lexi-lambda/eff">eff</a>, and the excellent work from others they have inspired. I am proud of this very blog, from the <a href="/blog/2019/11/05/parse-don-t-validate/">most impactful</a> things I’ve written to the <a href="/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/">far more niche</a>. I am proud of my <a href="https://langdev.stackexchange.com/users/861/alexis-king?tab=answers&amp;sort=newest">contributions to Programming Languages Stack Exchange</a>, a site I most certainly intend to remain a moderator of and will undoubtedly continue to contribute to. And I am proud of the <a href="https://www.youtube.com/watch?v=0jI-AlWEwYI">numerous talks</a> I have <a href="https://www.youtube.com/watch?v=TE48LsgVlIU">presented over the years</a>, all of them labors of love.</p><p>I have not always accomplished all the things I set out to achieve. Some have, for some reason or another, gotten the better of me. Many of them, from my <a href="https://github.com/lexi-lambda/blackboard">math typesetting system</a> to my <a href="https://github.com/ghc-proposals/ghc-proposals/pull/303">improvements to GHC’s arrow notation</a>, are things I hope to one day push over the finish line. But for now it is clear my heart is no longer really in them, so I think it is time for me to let them go. Perhaps others will find inspiration in them. Perhaps you will succeed where I did not.</p><p>It is a little sad to think about moving on from such a substantial chapter of my life. To date, I have spent almost my entire career in this professional niche, and I suspect I will never fully leave it. It has been a pleasure to work with so many bright, lovely, inspiring people, and perhaps one day I will find myself eager to return once more. But for now, my interests are elsewhere, and despite the bittersweet feelings, there is also a very real excitement in the tantalizing opportunity to do something new.</p><h2><a name="what-next"></a>What next?</h2><p>It is a little amusing to think that what I find myself most drawn to after a decade of pursuing ever more exotic projects is something entirely mundane: I want to write utterly ordinary software. I want to work on a piece of software used by people to accomplish a task, and I want to take pride and satisfaction in doing it well.</p><p>I do not yet really have any idea what that might look like. It has been over six years since I last worked on anything that really feels like it could possibly fit that description, and even then, it was only one part of my job. Is it so strange that after spending fifteen years of my life trying everything I could to get away from spending all day wrangling a big ball of mud in a Java IDE, that experience sounds pretty cozy to return to?</p><p>In late 2022, I decided on a whim to reverse engineer and resurrect a 2008 Java browser game called <a href="https://github.com/lexi-lambda/shattered-plans">Shattered Plans</a>. While doing so, I had the opportunity to spend an awful lot of time in IntelliJ, and after years of acclimatizing to writing code in fanciful languages with nothing more than a plain text editor, a terminal emulator, and a dream, the experience was almost viscerally thrilling. They say the grass is always greener, but even if that is true, I think I’d like to find it out for myself.</p><p>More to the point, my life is just very different now from how it was for most of my twenties. I was single, and it was easy to justify spending almost all of my time thinking about code, whether on the clock or not. It was hardly unheard of for me to spend twelve or even sixteen hours at my desk, just tinkering on something that had captured my attention. It was fun, and I’m glad I did it, but I am also very ready to work a simple, regular job I don’t feel the need to permanently devote a large portion of my soul to. It isn’t very romantic, but then, I have other things for that.</p><p>If you think you have a position that meets those (admittedly extremely broad) criteria, and you think you might like to hire me, by all means: <a href="mailto:lexi.lambda@gmail.com">send me an email</a>. The worst I can do is not reply. And who knows? With the burden of my own expectations behind me, perhaps I’ll even blow the dust off this old blog of mine and start writing things again. As with my job, whatever I might choose to write next I cannot say. Either way, it’s a little exciting to be able to treat myself to a blank slate.</p><p>I could probably spend twenty thousand words writing in circles about all my thoughts and feelings on this subject. However, to be honest, I don’t especially want to. As this blog post has hopefully communicated, I would like to be done, and so this will have to be enough. Allow me to personally thank all of you who have indulged me by reading my disorganized ramblings to the end.</p><p>It’s been a long time. Here’s to another ten years.</p><ol class="footnotes"><li id="footnote-1"><p>Readers may find it clarifying to note that a great deal of my early programming experience was writing (then era-appropriate) Java 6. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I have often wondered if other engineering disciplines have anything to rival the collective overconfidence of the average Hacker News commenter. This is an earnest curiosity, not merely a rhetorical flourish; I do sometimes wonder what it is about my field of choice that engenders such disregard for the value of expertise. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more open2020-01-19T00:00:00Z2020-01-19T00:00:00ZAlexis King<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Empathy and subjective experience in programming languages2019-10-19T00:00:00Z2019-10-19T00:00:00ZAlexis King<article><p>A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.</p><p>Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?</p><p>I think about that question a lot.</p><h2><a name="2015-called-and-they-want-their-dress-back"></a>2015 called, and they want their dress back</h2><p>Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.</p><p>However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into <em>Star Wars</em>. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.</p><p>Take 2015’s phenomenon of “<a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did <em>this</em>—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.</p><h3><a name="when-something-objective-isn-t"></a>When something objective isn’t</h3><p>Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.</p><p>The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.</p><p>In the case of the dress, whether you <a href="https://en.wikipedia.org/wiki/Yanny_or_Laurel">heard “yanny” or “laurel,”</a> or whether you believe the <em>Sonic</em> games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?</p><p>I think the answer is absolutely, unequivocally <em>yes</em>.</p><h2><a name="subjectivity-in-programming-and-in-programming-languages-specifically"></a>Subjectivity in programming, and in programming languages specifically</h2><p>Quick question: which is better, functional or imperative programming?</p><p>My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?</p><p>Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?</p><p>I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> but I did happen to find a link to <a href="https://news.ycombinator.com/item?id=21282647">a recent discussion</a> on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?</p><p>In a <a href="https://news.ycombinator.com/item?id=21284383">branch of the discussion</a>, one user writes:</p><blockquote><blockquote><p>Haskell is great for business and great in production</p></blockquote><p>I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.</p><p>I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.</p></blockquote><p>The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:</p><blockquote><p>I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.</p><p>[…]</p><p>From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.</p></blockquote><p>Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:</p><blockquote><p>Haskell gives one plenty of rope to hang himself on complexity.</p><p>So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.</p></blockquote><p>Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:</p><blockquote><p>Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.</p><p>[…]</p><p>The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.</p><p>Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.</p></blockquote><p>That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?</p><h3><a name="the-unsatisfying-subjective-reality-of-programming-languages"></a>The unsatisfying subjective reality of programming languages</h3><p>You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?</p><p><em><strong>No!</strong></em></p><p>These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, <em>why?</em> Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.</p><p>While <a href="https://news.ycombinator.com/item?id=21284317">one commenter</a> in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” <a href="https://news.ycombinator.com/item?id=21284540">another</a> says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.</p><p>Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t <em>get it.</em> There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.</p><h3><a name="empathy-and-how-bad-results-come-from-good-intentions"></a>Empathy, and how bad results come from good intentions</h3><p>I’ll admit that these kinds of discussions aren’t <em>always</em> fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.</p><p>Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.</p><p>Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:</p><blockquote><p>Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.</p></blockquote><p>I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?</p><p>If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.</p><p>Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.</p><p>To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:</p><blockquote><p>It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.</p><p>Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.</p></blockquote><p>When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective <strong>without</strong> invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about <em>your own</em> experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.</p><p>It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.</p><p>Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be <em>happier.</em></p><ol class="footnotes"><li id="footnote-1"><p>This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is <em>blah</em>,” but you deserve better. So you, uh, get a <em>meta</em> snarky footnote instead. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Which, to be entirely fair, may well be as subjective as anything else in this blog post. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>A space of their own: adding a type namespace to Hackett2017-10-27T00:00:00Z2017-10-27T00:00:00ZAlexis King<article><p>As previously discussed on this blog, <a href="https://github.com/lexi-lambda/hackett">my programming language, Hackett</a>, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?</p><p>For now, at least, the answer is that Hackett will emulate Haskell: <strong>Hackett now has two namespaces</strong>. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.</p><h2><a name="why-two-namespaces"></a>Why two namespaces?</h2><p>Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.</p><p>This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="ss">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="ss">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="ss">c</span><span class="p">)))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly <em>annotate</em> when they wish to use a value from a different namespace:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">mapcar</span><span class="w"> </span><span class="nf">#&#39;</span><span class="nb">car</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="nv">c</span><span class="p">)))</span> +<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>The Common Lisp <code>#'x</code> reader abbreviation is equivalent to <code>(function x)</code>, and <code>function</code> is a special form that references a value in the function namespace.</p><p>While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.</p><p>However, Hackett is a little different from all of the aforementioned languages because Hackett has <em>types</em>. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is <em>always</em> syntactically unambiguous.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Even if types and values live in separate namespaces, there is no need for a <code>type</code> form a la CL’s <code>function</code> because it can always be determined implicitly.</p><p>For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span></code></pre><p>This defines a binding named <code>Tuple</code> at the type level, which is a <em>type constructor</em> of two arguments that produces a type of kind <code>*</code>,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> and another binding named <code>Tuple</code> at the value level, which is a <em>value constructor</em> of two arguments that produces a value of type <code>(Tuple a b)</code>.</p><p>But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor <code>tuple</code> instead of <code>Tuple</code>? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>This works fine. But what happens if the programmer decides to change the name of the <code>bar</code> value?</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">qux</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>Can you spot the bug? Disturbingly, this code <em>still compiles</em>! Even though <code>bar</code> is not a member of <code>Foo</code> anymore, it’s still a valid pattern, since names used as patterns match anything, just as the <code>y</code> pattern matches against any integer inside the <code>baz</code> constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: <code>(foo-&gt;integer (baz 42))</code> will still produce <code>0</code>, not <code>42</code>, since the first case always matches.</p><p>Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the <code>Tuple</code> example above, which is illegal if a programming language only supports a single namespace.</p><p>Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The <code>Tuple</code> example from above is perfectly legal Hackett.</p><h2><a name="adding-namespaces-to-a-language"></a>Adding namespaces to a language</h2><p>Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to <em>implement</em> such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?</p><p>Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: <strong>the remainder of this blog post is <em>highly technical</em>, and some of it involves some of the more esoteric corners of Racket’s macro system</strong>. This blog post is <em>not</em> representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.</p><h3><a name="namespaces-as-scopes"></a>Namespaces as scopes</h3><p>With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as <a href="https://www.cs.utah.edu/plt/scope-sets/"><em>sets of scopes</em></a>, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.</p><p>Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the <em>value scope</em>, and all identifiers in type position must have a different scope, which we will call the <em>type scope</em>. How do we create these scopes and apply them to identifiers? In Racket, we use a function called <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29"><code>make-syntax-introducer</code></a>, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can <em>add</em> the scope to all pieces of the syntax object, <em>remove</em> the scope, or <em>flip</em> the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call <code>make-syntax-introducer</code> once for each namespace:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>We define these in a <code>begin-for-syntax</code> block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Each of these two forms is like <code>begin</code>, which is a Racket form that is, for our purposes, essentially a no-op, but it applies <code>value-introducer</code> or <code>type-introducer</code> to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">value-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">type-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span></code></pre><p>This program produces the following output:</p><pre><code>'value-x +'type-x +</code></pre><p>It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.</p><p>However, although this is close, it isn’t <em>quite</em> right. What happens if we nest the two inside each other?</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span></code></pre><pre><code>x: identifier's binding is ambiguous + context...: + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) + #(190359 intdef) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) +</code></pre><p>Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of <code>x</code> is <em>ambiguous</em> because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of <code>begin/value</code> or <code>begin/type</code> to <em>override</em> outer ones, ensuring that a use can only be in a single namespace at a time.</p><p>To do this, we simply need to adjust <code>begin/value</code> and <code>begin/type</code> to remove the other scope in addition to adding the appropriate one:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Now our nested program runs, and it produces <code>'type-x</code>, which is exactly what we want—the “nearest” scope wins.</p><p>With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.</p><h3><a name="namespaces-that-cross-module-boundaries"></a>Namespaces that cross module boundaries</h3><p>The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.</p><p>Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the <em>other</em> module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export <em>symbols</em>, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named <code>foo</code>, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the <code>require</code> form’s job to attach the correct scopes to imported identifiers.</p><p>This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup> This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary <em>points</em> of having multiple namespaces.</p><p>What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s <code>require</code> and <code>provide</code> forms are extensible, it’s even possible to implement this mangling in a completely invisible way.</p><p>Currently, the scheme that Hackett uses is to prefix <code>#%hackett-type:</code> onto the beginning of any type exports. This can be defined in terms of a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29"><em>provide pre-transformer</em></a>, which is essentially a macro that cooperates with Racket’s <code>provide</code> form to control the export process. In this case, we can define our <code>type-out</code> provide pre-transformer in terms of <a href="https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29"><code>prefix-out</code></a>, a form built-in to Racket that allows prefixing the names of exports:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">type-out</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-provide-pre-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="w"> </span><span class="n">modes</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">pre-expand-export</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">prefix-out</span><span class="w"> </span><span class="n">#%hackett-type:</span> +<span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">type-introducer</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-out</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span> +<span class="w"> </span><span class="n">modes</span><span class="p">)]))))</span></code></pre><p>Note that we call <code>type-introducer</code> in this macro! That’s because we want to ensure that, when a user writes <code>(provide (type-out Foo))</code>, we look for <code>Foo</code> in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that <code>provide</code> knows <em>which</em> <code>Foo</code> is being provided.</p><p>Once we have referenced the correct binding, the use of <code>prefix-out</code> will appropriately add the <code>#%hackett-type:</code> prefix, so the exporting side is already done. Users do need to explicitly write <code>(type-out ....)</code> if they are exporting a particular type-level binding, but this is rarely necessary, since most users use <code>data</code> or <code>class</code> to export datatypes or typeclasses respectively, which can be modified to use <code>type-out</code> internally. Very little user code actually needs to change to support this adjustment.</p><p>Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the <code>#%hackett-type:</code> prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.</p><p>Just as we extended <code>provide</code> with a provide pre-transformer, we can extend <code>require</code> using a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29"><em>require transformer</em></a>. In code, this entire process looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">and~&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">regexp-match</span><span class="w"> </span><span class="sr">#rx"^#%hackett-type:(.+)$"</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="nb">second</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">unmangle-types-in</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-require-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">imports</span><span class="w"> </span><span class="n">sources</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">expand-import</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-in</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">match-lambda</span> +<span class="w"> </span><span class="p">[(</span><span class="k">and</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="n">local-id</span><span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">local-name</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol-&gt;string</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">local-id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">unmangled-type-name</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">local-name</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">unmangled-type-name</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">unmangled-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;symbol</span><span class="w"> </span><span class="n">unmangled-type-name</span><span class="p">)</span> +<span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="n">local-id</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">unmangled-id</span><span class="p">)</span> +<span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="n">i</span><span class="p">))])</span> +<span class="w"> </span><span class="n">imports</span><span class="p">)</span> +<span class="w"> </span><span class="n">sources</span><span class="p">)])))</span></code></pre><p>This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:</p><ol><li><p>It iterates over each import and calls <code>unmangle-type-name</code> on the imported symbol. If the result is <code>#f</code>, that means the import does not have the <code>#%hackett-type:</code> prefix, and it can be safely passed through unchanged.</p></li><li><p>If <code>unmangle-type-name</code> does <em>not</em> return <code>#f</code>, then it returns the unprefixed name, which is then provided to <code>datum-&gt;syntax</code>, which allows users to forge new identifiers in an <em>unhygienic</em> (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from <code>unmangle-type-name</code>, but with the lexical context of the original identifier.</p></li><li><p>Finally, we pass the new identifier to <code>type-introducer</code> to properly add the type scope, injecting the fresh binding into the type namespace.</p></li></ol><p>With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write <code>type-out</code> when exporting types, it is unlikely that users will want to write <code>unmangle-types-in</code> around each and every import in their program. For that reason, we can define a slightly modified version of <code>require</code> that implicitly wraps all of its subforms with <code>unmangle-types-in</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="p">(</span><span class="k">rename-out</span><span class="w"> </span><span class="p">[</span><span class="n">require/unmangle</span><span class="w"> </span><span class="k">require</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">require/unmangle</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-types-in</span><span class="w"> </span><span class="n">require-spec</span><span class="p">)</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>…and we’re done. Now, Hackett modules can properly import and export type-level bindings.</p><h3><a name="namespaces-plus-submodules-the-devil-s-in-the-details"></a>Namespaces plus submodules: the devil’s in the details</h3><p>Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of <em>submodules</em>.</p><p>Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.</p><p>Submodules in Racket come in two flavors: <code>module</code> and <code>module*</code>. The difference is what order, semantically, they are defined in. Submodules defined with <code>module</code> are essentially defined <em>before</em> their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with <code>module*</code> are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.</p><p>How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.</p><p>However, there is <a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29">a special sort of <code>module*</code> submodule that uses <code>#f</code> in place of a module language</a>, which gives a module access to <em>all</em> of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a <code>test</code> submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="c1">; not provided</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span> + +<span class="p">(</span><span class="k">module*</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">rackunit</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">check-equal?</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="mi">41</span><span class="p">)</span><span class="w"> </span><span class="mi">42</span><span class="p">))</span></code></pre><p>It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>Remember that <code>make-syntax-introducer</code> is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module <a href="https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29">instantiation</a>, as ensured by Racket’s <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">separate compilation guarantee</a>. This means that each module gets its <em>own</em> pair of scopes. This means the body of a <code>module*</code> submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.</p><p>Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we <em>can</em> preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29"><code>make-syntax-delta-introducer</code></a>, we can create a syntax introducer the adds or removes the <em>difference</em> between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for <code>value-introducer</code> and <code>type-introducer</code> to always operate on the same scopes on each module instantiation:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-value/type-introducers</span> +<span class="w"> </span><span class="n">value-introducer:id</span><span class="w"> </span><span class="n">type-introducer:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">scopeless-id</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">introducer-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">value-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">type-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">type-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-value/type-introducers</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="n">type-introducer</span><span class="p">)</span></code></pre><p>The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. <em>However</em>, code inside <code>begin-for-syntax</code> blocks is still re-evaluated every time the module is instantiated! This means we are <em>not</em> circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.</p><p>We still use <code>make-syntax-introducer</code> to create our two scopes, but critically, we only call <code>make-syntax-introducer</code> inside the <code>define-value/type-introducers</code> macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds <code>value-id</code> and <code>type-id</code> as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use <code>make-syntax-delta-introducer</code> to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.</p><p>By guaranteeing each namespace’s scope is always the same, even for different modules, <code>module*</code> submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.</p><h3><a name="the-final-stretch-making-scribble-documentation-namespace-aware"></a>The final stretch: making Scribble documentation namespace-aware</h3><p>As discussed in <a href="/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/">my previous blog post</a>, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.</p><p>In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!</p><p>Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.</p><p>To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with <code>t:</code> upon import, and they are bound to Scribble <a href="https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29"><em>element transformers</em></a> that indicate they should be typeset without the <code>t:</code> prefix. Fortunately, Scribble’s documentation forms <em>do</em> understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.</p><p>In practice, this means Hackett documentation must now include a proliferation of <code>t:</code> prefixes. For example, here is the code for a typeset REPL interaction:</p><pre><code class="pygments"><span class="n">@</span><span class="p">(</span><span class="n">hackett-examples</span> +<span class="w"> </span><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">square</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">t:-&gt;</span><span class="w"> </span><span class="n">t:Integer</span><span class="w"> </span><span class="n">t:Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">[[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="p">}])</span> +<span class="w"> </span><span class="p">(</span><span class="n">square</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span></code></pre><p>Note the use of <code>t:-&gt;</code> and <code>t:Integer</code> instead of <code>-&gt;</code> and <code>Integer</code>. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.</p><p>This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides <code>deftype</code> and <code>deftycon</code> forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to <code>t:</code>-prefixed identifiers to properly index documented forms. Similarly, <code>defdata</code> and <code>defclass</code> have been updated with an understanding of types.</p><p>The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of <code>defform</code> slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into <code>t:</code>-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.</p><h2><a name="analysis-and-unsolved-problems"></a>Analysis and unsolved problems</h2><p>When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).</p><p>For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom <code>require</code> form, types provided by a module’s <em>language</em> are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike <code>require</code>’s require transformers.</p><p>To circumvent this restriction, <code>#lang hackett</code>’s reader includes a somewhat ad-hoc solution that actually inserts a <code>require</code> into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.</p><p>That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do <em>any</em> special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully <a href="http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html">internalizes extra-linguistic mechanisms</a>.</p><p>As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:</p><ul><li><p><strong>Multi-parameter typeclasses</strong> are implemented, along with <strong>default typeclass method implementations</strong>.</p></li><li><p>Pattern-matching performs basic <strong>exhaustiveness checking</strong>, so unmatched cases are a compile-time error.</p></li><li><p>Hackett ships with a <strong>larger standard library</strong>, including an <code>Either</code> type and appropriate functions, an <code>Identity</code> type, a <code>MonadTrans</code> typeclass, and the <code>ReaderT</code> and <code>ErrorT</code> monad transformers.</p></li><li><p><strong>More things are documented</strong>, and parts of the documentation are slightly improved. Additionally, <strong>Hackett’s internals are much more heavily commented</strong>, hopefully making the project more accessible to new contributors.</p></li><li><p><strong>Parts of the typechecker are dramatically simplified</strong>, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.</p></li><li><p>As always, various bug fixes.</p></li></ul><p>Finally, special mention to two new contributors to Hackett, <a href="https://github.com/iitalics">Milo Turner</a> and <a href="https://github.com/Shamrock-Frost">Brendan Murphy</a>. Also special thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> and <a href="https://github.com/michaelballantyne">Michael Ballantyne</a> for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.</p><ol class="footnotes"><li id="footnote-1"><p>“But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have <code>#lang</code>. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate <code>#lang</code>, not a modified version of Hackett, so Hackett can optimize its user experience for what it <em>is</em>, not what it might be someday. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow <code>*</code> to be used to mean “type” at the kind level and “multiply” at the value level. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, <em>per phase</em>, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Realizing Hackett, a metaprogrammable Haskell2017-05-27T00:00:00Z2017-05-27T00:00:00ZAlexis King<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questions2017-01-05T00:00:00Z2017-01-05T00:00:00ZAlexis King<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheses2017-01-02T00:00:00Z2017-01-02T00:00:00ZAlexis King<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Climbing the infinite ladder of abstraction2016-08-11T00:00:00Z2016-08-11T00:00:00ZAlexis King<article><p>I started programming in elementary school.</p><p>When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to <a href="https://xkcd.com/974/">solve the general problem</a>. When I learned about programming, I was immediately hooked: it was <em>so easy</em> to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.</p><p>Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of <strong>abstraction</strong>.</p><h2><a name="the-brick-wall-of-inexpressiveness"></a>The brick wall of inexpressiveness</h2><p>When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:</p><pre><code class="pygments"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="p">;</span> +<span class="p">}</span> + +<span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span> +<span class="p">}</span></code></pre><p>This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.</p><p>It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The <em>whole reason</em> I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.</p><p>When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.</p><p>Both of these languages were flawed, as all languages are, but they opened my mind to the idea that <em>programming languages</em> could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.</p><h2><a name="discovering-lisp"></a>Discovering Lisp</h2><p>Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.</p><p>This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?</p><p>The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a <a href="https://github.com/lexi-lambda/libsol">highly dynamic Lisp with a prototypal object system called Sol</a>. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.</p><p>Unfortunately, it was also abysmally slow.</p><p>I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called <a href="http://racket-lang.org">Racket</a>. At about the same time, someone pointed me to a totally different language called <a href="https://www.haskell.org">Haskell</a>. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.</p><h2><a name="a-journey-into-complexity"></a>A journey into complexity</h2><p>Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned <em>so much more</em> since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.</p><p>I’ve also started realizing something else, though: <strong>the languages I’ve settled into are <em>really complicated</em>.</strong></p><p>When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.</p><p>Why?</p><p>Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”</p><p>Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?</p><p>I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.</p><p>Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I <em>still</em> have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?</p><h3><a name="improvement-but-never-mastery"></a>Improvement, but never mastery</h3><p>Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.</p><p>For example I am pretty confident that I <em>get</em> JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques <em>within</em> the language, not entirely new linguistic constructs.</p><p>On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about <code>TypeInType</code>? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do <em>not</em> have a good grasp on all the intricacies of Racket’s macro system.</p><p>This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out <code>DataKinds</code>, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.</p><p>Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is <em>terrible</em>, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.</p><p>Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even <em>more</em> advanced things, and still, none of them truly solve my problems.</p><h2><a name="ultimately-it-all-has-at-least-a-little-value"></a>Ultimately, it all has (at least a little) value</h2><p>It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.</p><p>The scariest part of all is that I think it’s completely worthwhile.</p><p>So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs <em>seem</em> unnecessarily complicated, they <em>seem</em> increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.</p><p>The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.</p><p>Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:</p><pre><code class="pygments"><span class="nl">10</span><span class="w"> </span><span class="kr">INPUT</span><span class="w"> </span><span class="s2">"What is your name: "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span> +<span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">"Hello "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span></code></pre><p>On the other hand, very few would probably understand this one:</p><pre><code class="pygments"><span class="c1">-- | A class for categories.</span> +<span class="c1">-- id and (.) must form a monoid.</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">Category</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="c1">-- | the identity morphism</span> +<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="c1">-- | morphism composition</span> +<span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">c</span></code></pre><p>Yet very few new programs are being written in BASIC, and lots are being written in Haskell.</p><p>Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:</p><pre><code class="pygments"><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composeWithProps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">curry</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">childProps</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">omit</span><span class="p">([</span><span class="s1">&#39;children&#39;</span><span class="p">],</span><span class="w"> </span><span class="nx">childProps</span><span class="p">),</span><span class="w"> </span><span class="nx">childProps</span><span class="p">.</span><span class="nx">children</span><span class="p">));</span> +<span class="w"> </span><span class="c1">// give the composed component a pretty display name for debugging</span> +<span class="w"> </span><span class="nx">composed</span><span class="p">.</span><span class="nx">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`Composed(</span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="si">}</span><span class="sb">, </span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">composed</span><span class="p">;</span> +<span class="p">});</span></code></pre><p>Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.</p><p>That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an <em>extremely</em> productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.</p><h3><a name="reactionary-anti-intellectualism-and-the-search-for-moderation"></a>Reactionary anti-intellectualism and the search for moderation</h3><p>I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. <a href="https://twitter.com/lexi_lambda/status/763111451691134976">I recently tweeted a picture of some rather dense mathematics from a paper I’d read</a>, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.</p><p>Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like <code>map</code> should be banned because they are too confusing and not immediately self-explanatory.</p><p>I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.</p><p>Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern <em>technology</em> possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.</p><p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.</p><p>I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.</p><p>Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/programming-languages.rss.xml b/feeds/programming-languages.rss.xml new file mode 100644 index 0000000..04dd6e7 --- /dev/null +++ b/feeds/programming-languages.rss.xml @@ -0,0 +1,254 @@ +Posts tagged ‘programming languages’ | Alexis King’s BlogPosts tagged ‘programming languages’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/programming-languages.html29 May 202529 May 202560A break from programming languageshttps://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/https://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/29 May 2025<article><p>This is a blog post I have been considering writing for a long time.</p><p>People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.</p><p>My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.</p><p>This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for <em>me</em>, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why <em>not</em>—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.</p><h2><a name="whence-programming-languages"></a>Whence programming languages</h2><p>I have had a personal fascination with programming languages for as long as I have been using them. I made my first serious forays into programming in the fifth grade, writing (very simple and mostly very bad) Flash games in ActionScript 3. I wrote my first programming language interpreter in my sophomore year of high school, a dynamically-typed, object-oriented Lisp with prototypal inheritance. The interpreter was written in C, and despite it being my first serious endeavor with the language, I used preprocessor macros to implement a form of exceptions in terms of <code>setjmp.h</code>. It is an amusing project in retrospect, given what my design sensibilities would eventually turn out to be, but it at least provides a little insight into how much the subject was already on my mind in my teenage years.</p><p>I have always deeply enjoyed programming. That is no less true now than it was fifteen years ago. As someone with (then untreated) ADHD, the automation of repetitive tasks has always held immediate appeal, but it did not take long to discover I enjoy programming for its own sake. It is inherently delightful to me to direct an infinitely reconfigurable machine towards any end I desire, given only a little thought and some time at a keyboard. Program <em>design</em>, even in the large, was something that immediately appealed to me. It is often as much a challenge of organizing one’s thoughts as it is a matter of translating them to a form the computer can understand, and the former is a challenge I have always found rewarding.</p><p>Even so, as every programmer inevitably discovers, the gap between one’s thoughts and executable code is never so narrow as we would like. I quickly discovered that a distressingly large amount of my time was being spent on the exactly the sort of repetitive task I had hoped to avoid, mechanically typing out reams of boilerplate in order to carry out what seemed to be incredibly simple tasks.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> It felt strange that we, as programmers, could be one of the lucky disciplines equipped with the means to produce our own tools, yet the tools we work with are so frequently unsatisfactory.</p><p>Programming is an astonishingly young discipline. FORTRAN, arguably the first recognizable programming language, is less than seventy years old, and the first stored-program computers are only a decade older. Programmers have been trying to bridge the gap between our conceptualizations of programs and the (often laborious) act of programming for as long as computing has existed. As John Backus said in 1979,</p><blockquote><p>Much of my work has come from being lazy. I didn’t like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs.</p></blockquote><p>In my experience, this sentiment perfectly captures the strange relationship many programming language enthusiasts have with our craft. We enjoy programming so much that we are willing to spend enormous time, thought, and effort working on programming systems precisely to free ourselves from the tortuous burden of writing programs.</p><p>One possible means of reconciling this apparent contradiction would be to hypothesize that programmers like me or like Backus enjoy the <em>results</em> of our programs but not the process of writing them. We want to get things done, and we view the computer as a useful but necessary evil that should get out of the way as much and as expediently as possible. However, although I am sure there are many programmers who do match that description, I do not count myself among them, and I would likewise find it extremely difficult to believe that Backus—a man who dedicated his life to designing ever more sophisticated means of expressing computer programs—was someone fundamentally uninterested in programming.</p><p>On the contrary, I see a great deal of work on programming languages as a struggle—often in vain—by those of us who love programming so much that we wish to free it of its unfortunate imperfections. We strive to remove the impediments and obstacles we face when translating the beautiful, platonic programs we hold in our heads into the comparatively cold, alien language of silicon calculating machines. There is a widespread mythology that programming languages are primarily abstractions over machine code, but this perspective has been misguided for almost as long as programming languages have existed. Programming languages are instruments of thought, frameworks for precisely specifying an executable idea, and they are designed almost entirely for <em>humans</em>, with computers often providing little more than a loose set of inconvenient restrictions.</p><h3><a name="to-compute-is-human-to-program-divine"></a>To compute is human, to program divine</h3><p>The earliest computers were developed with a singular purpose in mind: to compute. A program was, essentially, a sequence of instructions containing the computations to be performed. This conceptualization of a computer program as essentially analogous to a cooking recipe or assembly manual has remained curiously persistent, even to the present day.</p><p>Almost no modern programs of any complexity look anything like a cooking recipe. Even programs written in the most imperative programming languages under the sun are complex webs of logic specifying dataflow, access policy, and interaction patterns between semi-autonomous, reactive subsystems, many of which come equipped with their own living, beating hearts. At the micro scale, it is true that programs specify behavior as a series of steps to be performed, but even this is a fiction maintained by modern compilers for humans’ convenience. Through a compiler’s eyes, the <em>real</em> programs are whatever is left after the comforting fiction is boiled away to obtain a homogenous pile of SSA and dataflow graphs to be haphazardly reassembled into a patchwork of basic blocks in a language most programmers would not even begin to understand.</p><p>Indeed, in stark contrast to the mental model of the programmer as a sort of silicon whisperer, I believe most practicing software engineers are primarily maintaining vast behavioral specifications whose defining sense of rigor comes not from computers <em>per se</em> but from the consistency we demand from their results. In other words, we are trained to be fluent speakers of languages that allow us to articulate a solution to a problem with a level of precision that ensures every legitimate interpretation is and will always be to our satisfaction. Crucially, these specifications do not <em>fully</em> specify how the task is to be carried out. Optimizing compilers are free to make their own choices with whatever leeway we permit them. HTML/CSS user agents may interpret our instructions in whatever way preserves whatever web standards happen to require. Even RDBMS query planners, a foundational technology largely unchanged for the past several decades, are essentially expert systems designed to decide on the fly how best to execute searches and retrievals under ever-changing conditions.</p><p>This is not to say that the task of programming is fully removed from the machine that ultimately executes our instructions, nor is it even really a suggestion it ought to be. Performance and debugging mandate a certain familiarity with the underlying system, even if the full complexities of its true inner workings remain carefully hidden from even its expert users. My point is simply that programs are not rote algorithms, and the act of programming is not primarily characterized by algorithmic thinking. Rather, programming is fundamentally a game of domain modeling and system design, with bits of algorithmic logic inserted here and there to glue the whole thing together.</p><p>My interest in pushing the frontier of programming language design has always been and continues to be an interest in making the task of writing programs feel closer to the task of designing them. In an ideal world, I would have a language expressive enough to more or less directly translate the computer programs I have in my head (which, for me, more often than not take <em>visual</em> form) into a digitized structure a computer may faithfully execute. It is that task I found myself fixated on at the tender age of fifteen, and it is one I would remain fixated on for the better part of the following decade.</p><h2><a name="ten-years-of-programming-languages"></a>Ten years of programming languages</h2><p>I turned twenty eight this year. Almost exactly ten years ago, I started my first professional software engineering job. It was a fairly mundane position writing Ruby on Rails and JavaScript, but even then, I spent a great deal of free time pursuing the programming language hobby projects that truly caught my fancy. My first professional experience working with Haskell would come less than a year later, experience that would soon inspire my hobby research project to implement a Haskell-style type system within the Racket macro system. The majority of my career to date has followed more or less directly from that formative time.</p><p>After a decade, it seems fair to reflect a little on what I have learned, what I managed to achieve, and maybe more significantly, what I did <em>not</em>. Something one learns very quickly upon beginning any serious work on programming languages is that they are startlingly, unforgivably <em>hard</em>. A vast array of dizzyingly smart people have been working on the problem of program specification for seventy years; there are not many pieces of low hanging fruit left on the tree, and any new gains tend to be very hard won.</p><p>Still, even if programming language design is challenging, it would be some consolation if we were able to slowly yet steadily advance the state of the art, gradually picking away at the gap between the tools we have now and the ultimate programming language of the future. Sadly, even that pragmatic vision does not survive contact with reality. In practice, the challenge of programming language design is not one of expanding a well-defined frontier, it is grappling with a neverending list of fundamental <em>tradeoffs</em> between mutually incompatible features.</p><p>Subtyping is tantalizingly useful but makes complete type inference incredibly difficult (and in general, provably impossible). Structural typing drastically reduces the burden of assigning a <em>name</em> to every uninteresting intermediate form but more or less precludes taking advantage of Haskell-style typeclasses or Rust-style traits. Dynamic dispatch substantially assists decoupling of software components but can come with a significant performance cost without a JIT. Just-in-time compilation can use runtime information to optimize code in ways that permit more flexible coding patterns, but JIT performance can be unpredictable, and the overhead of an optimizing JIT is substantial. Sophisticated metaprogramming systems can radically cut down on boilerplate and improve program concision and even readability, but they can have substantial runtime or compile-time overhead and tend to thwart automated tooling. The list goes on and on.</p><p>The essential, most stubborn problems in programming languages come from unavoidable tensions between conflicting desires and requirements. We want loosely coupled software components that can be easily reused, but we also want the performance benefits of tight coupling and specialization. We want flexible programming languages that do not impose upon our freedom of expression, but we also want the benefits of static program analysis and powerful safety guarantees. We want sophisticated type systems that allow specifying ever more complex invariants, but we also want readable type signatures that won’t regularly end up longer than the code itself.</p><p>We cannot build the One True Programming Language because we cannot have everything at once, and we cannot hope to universally decide which tradeoffs are the right ones to make. This naturally makes the thought of a system constructed from a tapestry of many different programming languages attractive, affording the programmer the opportunity to select the tool best suited to the task at hand. Unfortunately, that, too, comes with (often brutal) tradeoffs of its own: the impedance mismatch at language boundaries, the vastly more complex tooling required for a polyglot system, and the increased knowledge burden of maintaining a system built in so many different ways.</p><p>Ten years of working on and thinking about programming languages has forced me to come to terms with a humbling truth: I do not know how to build a better programming language. I am personally extremely sympathetic to the idea that the future of programming probably involves more domain-specific languages, and I think the industry as a whole has been (very slowly) trending in that direction for quite some time. But even I acknowledge that the question of how precisely that ought to be done is extraordinarily complicated, and there are no easy answers here.</p><p>Even so, it would be far too dismal of me to suggest that the problem is hopeless, nor do I believe the work does not matter. Even if the problem has proven more difficult than teenage me may have hoped, the work <em>is</em> satisfying when it bears fruit. Unfortunately, even after toiling for years overcoming daunting problems, every working programming language enthusiast is forced to confront a much more frustrating enemy: <em>programmers</em>.</p><h3><a name="the-reactionary-conservatism-of-the-median-programmer"></a>The reactionary conservatism of the median programmer</h3><p>It should come as no surprise that programming language design is largely a social problem. Choice of programming language tends to be driven by strong network effects, and programmers tend to prefer languages that resemble what they already know. Programmers’ general lack of curiosity and outright hostility to learning new things has always been a personal frustration of mine, but I ultimately do understand much of where it comes from: as engineers, an overwhelming part of our job description is managing <em>risk</em>. Exotic technology choices are risky, and it is a responsible impulse to be wary of them.</p><p>Still, the history of mainstream programming languages is essentially a story of programmers vocally and emphatically rejecting what eventually proved to be some of the most incredibly successful innovations in the history of the field. Assembly programmers largely laughed at FORTRAN, but just a few decades later, there were nevertheless very few remaining assembly programmers. First-class functions were widely derided as needlessly complicated and confusing until programmers were forced to finally take the time to learn to use them once JavaScript became a load-bearing language by historical accident, and within a decade, they became a required feature for every major programming system. Sophisticated type systems largely retain a perception of overengineered, ivory-tower elitism, but many of the programmers who hold those very opinions have enthusiastically adopted Rust, a language that features a type system so complex that idiomatic Rust code can easily put Haskell programs to shame.</p><p>Discussions of programming languages are, by and large, emotionally driven shouting matches based on anecdotes and gut feelings. Surprisingly (or perhaps not), although most working programming language theorists and compiler engineers have a far more informed perspective on this subject (and do at least tend to refrain from petty debate), disparaging remarks directed towards languages not to the speaker’s taste are hardly unheard of even at academic conferences dedicated to the subject. I want to be very clear that I do not think that is somehow indicative of some toxicity within the community—they are by and large a group of truly amazing, wonderful people, and such comments are usually delivered with tongue planted firmly in cheek. All the same, my point is simply that programming languages are an emotional, opinionated subject in a way that seems perhaps inherent to the craft, <a href="/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/">a topic I explored at some length all the way back in 2019.</a></p><p>I do earnestly believe that the field of software engineering would benefit from more openness to new ideas and willingness to experiment with new technologies. I also think it would be enormously refreshing if programmers did not nearly universally speak with an expert’s confidence on subjects they have only passingly encountered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> Nevertheless, I do not expect programmers’ tribalism to diminish anytime soon, so the pragmatist in me understands their nature imposes real limitations on what we as a field can meaningfully accomplish.</p><p>In my experience, most working programming language researchers have essentially resigned themselves to this fact, and the prevailing wisdom is to almost entirely decouple the value of the work from its impact. Even if an idea is broadly ignored by programmers today, if it’s a good enough idea, it will still make its way into some programming language in some distant tomorrow, as many ideas eventually have. Lexical closures were first applied to programming languages in 1975 but took a full thirty years to be broadly accepted. Algebraic datatypes were first introduced around the same time and have only recently found mainstream acceptance through Rust. Innovations take a long, <em>long</em> time to make the jump from research to practice in this field, and placing one’s sense of professional accomplishment in the fickle hands of mainstream programmers’ whims and preferences is a recipe for personal misery.</p><p>I have nothing but respect for this attitude, genuinely. This research community is astonishingly friendly, warm, creative, fun, and accepting, especially given the vitriol these subjects tend to elicit from programmers at large. I feel genuinely honored to have had the opportunity to personally collaborate with many of them, and the collective output of endlessly fascinating ideas and exciting projects one can find each and every year at every major PL conference is consistently inspiring. All the same, as much respect as I have for the people who work tirelessly on these problems for their own sake, I am not sure I can continue to count myself among them. I do find the subject exciting, and I do consider it rewarding in its own way, but these past few years, I have slowly been coming to terms with the fact that I don’t think I find it quite rewarding enough.</p><h2><a name="why-i-write-computer-programs"></a>Why I write computer programs</h2><p>I like writing software. It is strange how radical a statement that sometimes feels. So many programmers seem to practically despise what they do for a living, at least as you’d hear them describe it. I have never really been able to empathize with that mindset. I find programming fun, and I derive a unique joy from writing code that decisively solves the problem at hand. It doesn’t have to be a particularly interesting problem, and the solution doesn’t even have to require much more than snapping blocks together, but as long as I can take some pride and satisfaction in the craftsmanship and end result, I am usually pretty happy.</p><p>My attraction to libraries, tooling, and compilers has always stemmed from a mixture of personal motivation and a certain satisfaction from improving things for my peers. One of my favorite things about software engineering is taking a problem and solving it in such a way that it largely <em>stays solved</em>. Sure, it may need to change as requirements evolve, but the solution continues to work even after the labor is done. Other tasks in my life do not provide nearly the same sense of accomplishment. When I do a load of laundry, it provides me with clean clothes for a week or two, but ultimately, I know it won’t be long before I’m going to have to do it again. Software engineering is not like that. Working on software—even extremely mundane software—provides a pleasant variety of tasks that can be well and truly <em>completed</em>. I like that sense of progress, and I appreciate the relaxing, almost meditative process of growing and maintaining the metaphorical garden that is a collection of code.</p><p>Given all of the above, it is not altogether clear why I would struggle to take satisfaction in my work on programming languages. If anything, working on a compiler seems like the ultimate realization of my aforementioned desires applied to the very thing I, myself, spend my day doing. If there is some annoying or repetitive task, and I can extend my programming system to provide a better way of doing it, that task is something I will never again need to do. What’s more, working on a compiler that thousands of people actually use means this is not just benefiting me, it’s benefiting everyone else as well. It feels good to have that kind of impact, and to know that my effort is essentially multiplied by everyone who happens to use it.</p><p>On paper, this ought to make working on a compiler like GHC enormously satisfying. Even if I feel frustrated by mainstream attitudes towards programming languages, Haskell programmers feel differently. Surely, working on something used and appreciated by Haskellers ought to feel very rewarding. But it wasn’t really. There were parts that were nice, yes, and the problems were often interesting. But I didn’t find it <em>rewarding</em> in the way I had hoped, and I’m not sure it even compared favorably to working on bog-standard software. Why?</p><h3><a name="on-the-haskell-community"></a>On the Haskell community</h3><p>This was the hardest section of this blog post to write. It is easy to write about the things I love and the people I admire. It is perhaps even easier to write about the things I despise and the people I disdain. The Haskell community is neither. I do not <em>dislike</em> it. Several of its members are people I consider very good friends. Nonetheless, I have never managed to fall in any particular love with it, and even if that is no sin on its part, it is a somewhat sad thing to have to confess.</p><p>I want to be <em>excruciatingly</em> clear that I do not wish to mount any indictment of the Haskell community or any of the people in it. Many people seem to have a preconception of Haskellers as mean and elitist, and while I do get the sense there have been some historically bad actors that contributed to that perception, my general impression is that those people have been thoroughly expunged. I have never been the target of any memorably rude conduct, and I’ve definitely never felt targeted by any prejudice or discrimination, and if anything I have always felt universally warmly welcomed at every meetup or conference I have attended. I cannot speak for anyone but myself, and my experiences are necessarily limited. I cannot hope to claim with any certainty that no abuse has ever occurred or that it does not continue to occur within any Haskell’s myriad social spaces, most of which I have never participated in. Still, I am happy to be able to say that I have never personally experienced any real unfriendliness, and that is certainly not a factor in my dispassion for the language’s community. If anything, I feel I must extend a very genuine <em>thank you</em> for all the lovely support I have received from Haskellers over the years, and it is that support and enthusiasm that so often kept me motivated for as long as I was.</p><p>This is all simply to say that I do not think it is the <em>fault</em> of anyone in particular for my lack of affinity for the Haskell community. Rather, I think perhaps it is just not a culture that I have ever especially related to. That probably does not even say very much, as I’m not sure there are many communities I <em>could</em> say that about. That says much more about me than it does about Haskell or Haskellers.</p><p>It is admittedly tempting to say that perhaps I find these communities alienating in part because they lack people I identify with and relate to. After all, it is simply true that the Haskell community is <em>overwhelmingly</em> male, which really has felt lonely at times. It’s also true that the Haskell community tends to attract a lot of people who are mainly interested in Haskell because they find it intellectually interesting, or because they like category theory and enjoy libraries like <code>lens</code> that have explored those ideas, while I am really only interested in Haskell insofar as it is a practical vehicle for writing useful software. Nevertheless, there <em>are</em> other women who write Haskell and there <em>are</em> other people who care very much about writing useful software in Haskell, and that doesn’t seem to have radically altered my relationship to the community, so I think it would be disingenuous of me to claim that either of those flaws are substantially to blame.</p><p>If there is one substantive frustration I can express with Haskell, it is where the money comes from. For as long as I have been involved with it, Haskell has predominantly been used and funded by a combination of fintech and cryptocurrency. I don’t really have anything against fintech, though I certainly don’t find it especially exciting, but I do have something against crypto, and those who followed me on Twitter before its untimely demise know I have always been pretty open about that fact. My thoughts on the matter are really not something I want to elaborate on here, but suffice to say it is not especially motivating to work on a language if all of its users are applying it in ways I feel essentially indifferent about at best and actively hostile to at worst. This is obviously not something I have any easy answers to, but it is probably the most significant, concrete factor in me losing my interest in working on GHC, so if you mourn me leaving, at least I can give you one semi-actionable reason.</p><p>Make no mistake: GHC is a truly incredible project that often still feels like a compiler from the future, and having the opportunity to work on it as a part of my job was about as technologically stimulating as I could possibly ask for. Even so, during my time at Tweag, I often found myself reflecting on the fact that the <em>users</em> I was working for were, ultimately, Haskell programmers, a class I’m not sure even really contains <em>me</em>. I have often reflected on the fact that, although I have many personal projects, I have <em>never</em> chosen Haskell for any of them. Not once. Whenever I need to write a little code to accomplish some task, it’s always Racket. This blog is in Racket. My personal shell utilities are in Racket. When I want to scrape a website or wrap some library, I do it with Racket. Haskell is a language I have always exclusively written professionally, and although I do very earnestly love many things about it, it is not so precious to me that I feel motivated to contribute to it out of passion alone. If there were more <em>users</em> of Haskell doing inspiring, exciting things with the tools I was working on, I might feel substantially more driven to indirectly contribute to those causes. But Haskell is a niche language used by a select few, and it is hard for me to spend so much of my life working on a technological marvel that practically nobody will ever benefit from.</p><p>As a final note on this subject: I do believe very earnestly that the functional programming and programming languages communities would benefit enormously from more demographic diversity. Even compared to software engineering as a whole, the gender ratio is, frankly, almost unbelievably grim. I have always been drawn to interests and hobbies that tend to skew male, and that has never really bothered me, so if <em>I</em> have found myself feeling alienated by the conspicuous absence of other women—to the point that, in the past, I have sometimes considered giving up on Haskell primarily for that reason—I don’t think it is outrageous to say things are pretty dire. A wider set of perspectives could, in theory, inject some refreshing creativity into the dreary ecosystem that is industrial Haskell. Of course, I will admit that I have absolutely no idea how that end could possibly be achieved, and it is a subject far broader and more complicated than I think belongs in this blog post. For now, I will leave things at that, but I hope my voice is sufficiently well regarded that some readers may take my comments to heart.</p><h2><a name="reflections-on-a-decade-s-work"></a>Reflections on a decade’s work</h2><p>Before writing this blog post, I essentially pitched it to a group of friends as “a sort of apology”. At times I certainly feel burdened by the weight of all the endeavors I’ve left unfinished, all the work I never carried long enough to bear fruit. Even so, as I write these words, I find myself reflecting on the things I feel I <em>did</em> manage to contribute these past ten years, and it strikes me that the trail I’ve left behind is perhaps not so dismal, after all.</p><p>It is comforting to look back on a decade’s accumulations and realize there is enough to take some real pride in. I am proud of my contributions to Racket and Haskell, and of my numerous libraries in each of them. I am proud of my experiments, among them <a href="https://github.com/lexi-lambda/hackett">Hackett</a> and <a href="https://github.com/lexi-lambda/eff">eff</a>, and the excellent work from others they have inspired. I am proud of this very blog, from the <a href="/blog/2019/11/05/parse-don-t-validate/">most impactful</a> things I’ve written to the <a href="/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/">far more niche</a>. I am proud of my <a href="https://langdev.stackexchange.com/users/861/alexis-king?tab=answers&amp;sort=newest">contributions to Programming Languages Stack Exchange</a>, a site I most certainly intend to remain a moderator of and will undoubtedly continue to contribute to. And I am proud of the <a href="https://www.youtube.com/watch?v=0jI-AlWEwYI">numerous talks</a> I have <a href="https://www.youtube.com/watch?v=TE48LsgVlIU">presented over the years</a>, all of them labors of love.</p><p>I have not always accomplished all the things I set out to achieve. Some have, for some reason or another, gotten the better of me. Many of them, from my <a href="https://github.com/lexi-lambda/blackboard">math typesetting system</a> to my <a href="https://github.com/ghc-proposals/ghc-proposals/pull/303">improvements to GHC’s arrow notation</a>, are things I hope to one day push over the finish line. But for now it is clear my heart is no longer really in them, so I think it is time for me to let them go. Perhaps others will find inspiration in them. Perhaps you will succeed where I did not.</p><p>It is a little sad to think about moving on from such a substantial chapter of my life. To date, I have spent almost my entire career in this professional niche, and I suspect I will never fully leave it. It has been a pleasure to work with so many bright, lovely, inspiring people, and perhaps one day I will find myself eager to return once more. But for now, my interests are elsewhere, and despite the bittersweet feelings, there is also a very real excitement in the tantalizing opportunity to do something new.</p><h2><a name="what-next"></a>What next?</h2><p>It is a little amusing to think that what I find myself most drawn to after a decade of pursuing ever more exotic projects is something entirely mundane: I want to write utterly ordinary software. I want to work on a piece of software used by people to accomplish a task, and I want to take pride and satisfaction in doing it well.</p><p>I do not yet really have any idea what that might look like. It has been over six years since I last worked on anything that really feels like it could possibly fit that description, and even then, it was only one part of my job. Is it so strange that after spending fifteen years of my life trying everything I could to get away from spending all day wrangling a big ball of mud in a Java IDE, that experience sounds pretty cozy to return to?</p><p>In late 2022, I decided on a whim to reverse engineer and resurrect a 2008 Java browser game called <a href="https://github.com/lexi-lambda/shattered-plans">Shattered Plans</a>. While doing so, I had the opportunity to spend an awful lot of time in IntelliJ, and after years of acclimatizing to writing code in fanciful languages with nothing more than a plain text editor, a terminal emulator, and a dream, the experience was almost viscerally thrilling. They say the grass is always greener, but even if that is true, I think I’d like to find it out for myself.</p><p>More to the point, my life is just very different now from how it was for most of my twenties. I was single, and it was easy to justify spending almost all of my time thinking about code, whether on the clock or not. It was hardly unheard of for me to spend twelve or even sixteen hours at my desk, just tinkering on something that had captured my attention. It was fun, and I’m glad I did it, but I am also very ready to work a simple, regular job I don’t feel the need to permanently devote a large portion of my soul to. It isn’t very romantic, but then, I have other things for that.</p><p>If you think you have a position that meets those (admittedly extremely broad) criteria, and you think you might like to hire me, by all means: <a href="mailto:lexi.lambda@gmail.com">send me an email</a>. The worst I can do is not reply. And who knows? With the burden of my own expectations behind me, perhaps I’ll even blow the dust off this old blog of mine and start writing things again. As with my job, whatever I might choose to write next I cannot say. Either way, it’s a little exciting to be able to treat myself to a blank slate.</p><p>I could probably spend twenty thousand words writing in circles about all my thoughts and feelings on this subject. However, to be honest, I don’t especially want to. As this blog post has hopefully communicated, I would like to be done, and so this will have to be enough. Allow me to personally thank all of you who have indulged me by reading my disorganized ramblings to the end.</p><p>It’s been a long time. Here’s to another ten years.</p><ol class="footnotes"><li id="footnote-1"><p>Readers may find it clarifying to note that a great deal of my early programming experience was writing (then era-appropriate) Java 6. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I have often wondered if other engineering disciplines have anything to rival the collective overconfidence of the average Hacker News commenter. This is an earnest curiosity, not merely a rhetorical flourish; I do sometimes wonder what it is about my field of choice that engenders such disregard for the value of expertise. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more openhttps://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/19 Jan 2020<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Empathy and subjective experience in programming languageshttps://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/https://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/19 Oct 2019<article><p>A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.</p><p>Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?</p><p>I think about that question a lot.</p><h2><a name="2015-called-and-they-want-their-dress-back"></a>2015 called, and they want their dress back</h2><p>Humans have a knack for caring intensely about the most trivial of things. Name almost anything—cats versus dogs, the appropriate way to fasten a necktie, or even which day of the week comes first—and someone somewhere has probably written an essay about it on an internet forum. It would be easy to throw up our hands and give up trying to understand our peers, as sometimes they seem like aliens from another planet.</p><p>However, what interests me is how the littlest things seem to get people the most upset. Few people have shouting matches over the best interpretation of quantum mechanics, but friendships will be tested when someone says they just aren’t that into <em>Star Wars</em>. One explanation for this phenomenon is simple accessibility: most people aren’t equipped to understand quantum mechanics well enough to argue about it, but almost anyone can have an opinion on which direction the toilet paper is supposed to go.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>There is truth in that explanation, but personally, I don’t think it’s the whole story. Rather, I think we grow so used to the idea that our experiences are universal that discovering someone else experienced the exact same thing we did yet came to a different conclusion is not just frustrating: it’s incomprehensible.</p><p>Take 2015’s phenomenon of “<a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>” as an example. Some people see black and blue, others white and gold, and frankly, whether you see one or the other has no impact on anything remotely meaningful. How did <em>this</em>—something so completely irrelevant—become a cross-cultural phenomenon reported on by major news outlets? My guess: people just aren’t used to the idea that vision—the primary way we sense the world—does not provide us with an objective, universal understanding of reality.</p><h3><a name="when-something-objective-isn-t"></a>When something objective isn’t</h3><p>Our culture and society works because, in spite of our differences, we’re still all humans. We eat food, we sleep, we like spending time with each other, and we like feeling connected to those around us. So when we watch a movie, and it tickles us in a way that makes us feel good, we can have an awfully hard time understanding how our best friend—who we largely agree with about everything—didn’t like it at all.</p><p>The truth, of course, is that very little of what we experience is in any way objective. Yes, we can be pretty confident that basic arithmetic is true anywhere in the universe, and that if we all agree a table is brown it probably is. There are even things we accept as subjective without a second thought, such as the kinds of food people like or the fashions they find attractive. It’s all the in-betweens that are so pernicious! “The dress” was so unbelievable to most people because, nine hundred and ninety nine times out of of a thousand, when two humans look at a picture, they at least mostly agree on the colors contained within. We do not consider that we are seeing different lenses into the same objective reality, we simply think we are perceiving objective truths directly.</p><p>In the case of the dress, whether you <a href="https://en.wikipedia.org/wiki/Yanny_or_Laurel">heard “yanny” or “laurel,”</a> or whether you believe the <em>Sonic</em> games were ever any good, subjective disagreement is essentially harmless. But what about when it isn’t? Might incorrect beliefs that our experiences are universal cause genuine harm?</p><p>I think the answer is absolutely, unequivocally <em>yes</em>.</p><h2><a name="subjectivity-in-programming-and-in-programming-languages-specifically"></a>Subjectivity in programming, and in programming languages specifically</h2><p>Quick question: which is better, functional or imperative programming?</p><p>My guess, given the usual subject of my blog, most of my readers would pick the former. However, the actual answer you chose doesn’t matter: my guess is you feel like you have a pretty rational argument to back it up. It certainly isn’t simply a matter of taste… right?</p><p>Well, no, I hope not. I don’t think the world is so subjective that we cannot ever advocate for one thing over another—we tried that whole “everything is XML” thing for a while, and I think we agreed it really wasn’t a good idea. But if you truly believe your answer to the above question can be completely objectively justified (as many do), how does one explain the average Hacker News comment thread on just about any post about Haskell?</p><p>I generally try not to read Hacker News if I can help it, as I find doing it mostly just makes me angry,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> but I did happen to find a link to <a href="https://news.ycombinator.com/item?id=21282647">a recent discussion</a> on a blog post about using Haskell in production. Let’s take a look at a few comments, shall we?</p><p>In a <a href="https://news.ycombinator.com/item?id=21284383">branch of the discussion</a>, one user writes:</p><blockquote><blockquote><p>Haskell is great for business and great in production</p></blockquote><p>I disagree. It's a beautiful language but it lacks a lot of features to make it useable at scale. And in my experience Haskell engineers are extremely smart but the environment/culture they create makes it difficult to foster team spirit.</p><p>I've been in 2 companies in the last 4 years who initially used Haskell in production. One has transitioned to Go and the other is rewriting the codebase in Rust.</p></blockquote><p>The first paragraph is an assertion without many specifics, but it does sound like it could be reasonable. And although the last two sentences are entirely anecdotal, anecdotes are still better than hunches. Let’s see what someone else has to say in response:</p><blockquote><p>I’ve met some pretty damn solid engineers who started on Haskell and, even at a junior level in other languages, produce an elegant solution far more easily than a senior engineer in that language. You probably wouldn’t put the code in production verbatim but you can very easily see what’s going on and it isn’t haunted by spectre of early abstraction, which IMO is the biggest flaw of OOP at scale.</p><p>[…]</p><p>From my naive perspective it’s easy to make classes out of everything, and to hold state and put side-effects everywhere, but you don’t want to deal with the trouble of a monad until you need it. So you have an automatic inclination towards cleaner code when you start functional and move on.</p></blockquote><p>Also pretty vague and high-level, but also sounds reasonable. If you read either of these comments, and your first inclination was to grow frustrated and start crafting counter-arguments in your head, I encourage you to step outside your feelings momentarily (rational as they may be!) and try your very hardest to interpret them charitably. The discussion continues:</p><blockquote><p>Haskell gives one plenty of rope to hang himself on complexity.</p><p>So much that developers develop an aversion to it as deep as fear. It's unavoidable, the ones that didn't develop it are still buried at the working of their first Rube Goldberg machine and unavailable.</p></blockquote><p>Whether you think it’s accurate or not, there is definitely a perception held by a great many people that Haskell is a very complicated language. Surely at least some of them must have given it an honest shot, so have they just not “seen the light” yet? What do you think they’re missing? Perhaps a followup commenter can help elucidate things:</p><blockquote><p>Hi, I find that everything people here are complaining about (and they're valid complaints) has also been true of C++. C++ developed a lot of its complexity (particularly 15-20 yrs ago in the template space) after it got popular, so people were already wed to it.</p><p>[…]</p><p>The C++ community's really gotten good in the last 5 years or so about reigning in the bad impulses and getting people to write clean, clear, efficient code that has reasonable expressiveness.</p><p>Coming into Haskell from C++, I have the same instincts. Haskell's been a pure pleasure. The benefits are really there, and they're easy to get. You just have to think of the trade-offs.</p></blockquote><p>That argument seems reasonable, too. Everything in moderation, right? If you disagree, and you think Haskell is just not worth it, what does this person value that you don’t? What are they missing that you see?</p><h3><a name="the-unsatisfying-subjective-reality-of-programming-languages"></a>The unsatisfying subjective reality of programming languages</h3><p>You can probably see where I’m going with all this. These arguments are not built on hard, refutable facts or rigorous real-world evidence, they’re based in gut feelings and personal preferences. Does that mean they’re wrong, invalid, and worthless, and we should do studies to determine which language allows programmers to ship features the fastest and with the fewest bugs, then all agree to use that?</p><p><em><strong>No!</strong></em></p><p>These conversations are subjective because, for better or for worse, humans think in different ways and value different things, and programming languages are the medium in which we express ourselves. To many people who write Haskell (myself included), there is an effervescent joy in modeling a problem with the type system—like capturing something in amber—that others just don’t care about. What’s more, some people clearly loathe Haskell’s significant whitespace and plethora of infix operators, but I’ve never really minded. Is one of us wrong? If so, <em>why?</em> Talk about reliability all you want, but the few rigorous numbers we have don’t provide much evidence one way or the other.</p><p>While <a href="https://news.ycombinator.com/item?id=21284317">one commenter</a> in the aforementioned Hacker News thread described Haskell as nothing less than “pain and torture,” <a href="https://news.ycombinator.com/item?id=21284540">another</a> says they “did some Haskell in production and it was delightful.” People push excuses and rationalizations for these differences constantly—they point out that most people are exposed to imperative programming first, while others retort that Haskell is clearly not very widely used despite being around for an awfully long time—but none of their arguments ever seem to change people’s minds.</p><p>Often, people walk away from these conversations confused and incensed. To them, their point of view is so obviously apparent that it is hard to fathom anyone else seeing things differently. They rack their brains trying to figure out why their opponents just don’t <em>get it.</em> There must be some key point they’ve misunderstood, some joy they haven’t experienced, some sharp edge they haven’t yet been cut by. But no matter how much time they spend trying to reach these people, somehow, it’s never enough.</p><h3><a name="empathy-and-how-bad-results-come-from-good-intentions"></a>Empathy, and how bad results come from good intentions</h3><p>I’ll admit that these kinds of discussions aren’t <em>always</em> fruitless; sometimes they really do manage to change people’s minds or help them see some new idea they had not been able to grasp. When people manage to keep their cool and acknowledge the differences in their mindsets while still helping people learn, everyone benefits.</p><p>Sadly, in my experience, this rarely happens. We have a natural tendency to become angry if people don’t see things the way we do; it’s confusing and disorienting, and it can even disgust us. None of those emotions are conducive to empathy. When we fail to account for the ways in which others might think differently, we voluntarily reject any insights we might have otherwise gained from the conversation because we did not allow ourselves to embrace, even just temporarily, someone else’s strange and perhaps uncomfortable set of values and experiences. We refuse to accept that our perception of color might not be as universal as we thought, and we miss out on the amazing insights we could learn about the nature of light, color, and human vision.</p><p>Although failing to empathize with those we are arguing with is bad enough, in my mind, this failure to accept the potential subjectivity of one’s own views has even worse, indirect effects. Take this comment for example, again from the same thread:</p><blockquote><p>Sounds like you've barely programmed in Haskell and don't know what you're talking about. Haskell was the first language I learned. I didn't think this at all and I still don't. It doesn't strike me as any more difficult than learning Java or something.</p></blockquote><p>I have no doubts that this commenter meant what they said: they didn’t find Haskell difficult to learn. The comment they were replying to was vitriolic and combative, so one could almost feel they had a smackdown coming to them… but this isn’t a private conversation. How do you think someone feels when they are learning Haskell, scroll through this thread, and find a comment that tells them they ought to find it easy? If they’ve been struggling, even a little bit, what do you think they might think?</p><p>If I were in their place, I might feel a little stupid. I might wonder if I’m really cut out for Haskell or if I should just give up. I definitely wouldn’t feel encouraged and excited to keep trying.</p><p>Who knows why this commenter found Haskell straightforward. Maybe they were exposed to certain concepts already, maybe it just fit their style of thinking, perhaps they’re even exceptionally smart. I don’t know. But no matter what the answer is, insulting the intelligence of others, even indirectly in this way, belies a lack of empathy in the face of frustration, and although the intent may not have been to hurt, it can still be seriously harmful.</p><p>To be clear, I’m not saying the commenter should have pretended their experiences were different or even kept them to themselves. I don’t believe in being “fake nice”—in my experience, I am best equipped to reach people when speaking genuinely, from the heart. What I would have done is tell my story in a different way, perhaps by writing something like this:</p><blockquote><p>It’s true that a lot of people find Haskell challenging, and I totally accept that some people just don’t think it’s worth it. It’s fine if you don’t want to write Haskell. But personally, I really enjoy writing it, as do the people I work with, and I think we ship great software with it because it aligns naturally—even joyfully!—with the way we like to think about program construction.</p><p>Personally, I didn’t find Haskell as challenging to learn as I think some people have, but it was still work, and in some ways I was just exposed to it at the right time. Other people I know have struggled quite a lot at first, and reasonably so, but they’ve still managed to become great Haskell programmers, and they found it worthwhile. Our team dynamic just wouldn’t be the same in any other language.</p></blockquote><p>When I respond to comments I disagree with, I try to tell a personal story that provides a different perspective <strong>without</strong> invalidating their experiences. Sometimes the result is ungrateful snark anyway (or just no response at all), but you might be surprised how often talking from an emotional place about <em>your own</em> experiences—while being neither aggressive nor especially defensive—can go a long way. Perhaps you can even learn something if they return the favor and explain what they find frustrating, beyond the fundamental, subjective disagreements.</p><p>It’s okay to have opinions. It’s okay to like and dislike things. It’s okay to be frustrated that others don’t see things the way you do, and to advocate for the technologies and values you believe in. It’s just not okay to tell someone else their reality is wrong.</p><p>Learn to embrace the subjective differences between us all, and you won’t just be kinder. You’ll be <em>happier.</em></p><ol class="footnotes"><li id="footnote-1"><p>This is where I’m supposed to put a snarky footnote saying something like “obviously, the correct way is <em>blah</em>,” but you deserve better. So you, uh, get a <em>meta</em> snarky footnote instead. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Which, to be entirely fair, may well be as subjective as anything else in this blog post. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>A space of their own: adding a type namespace to Hacketthttps://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/https://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/27 Oct 2017<article><p>As previously discussed on this blog, <a href="https://github.com/lexi-lambda/hackett">my programming language, Hackett</a>, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?</p><p>For now, at least, the answer is that Hackett will emulate Haskell: <strong>Hackett now has two namespaces</strong>. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.</p><h2><a name="why-two-namespaces"></a>Why two namespaces?</h2><p>Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.</p><p>This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="ss">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="ss">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="ss">c</span><span class="p">)))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly <em>annotate</em> when they wish to use a value from a different namespace:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">mapcar</span><span class="w"> </span><span class="nf">#&#39;</span><span class="nb">car</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="nv">c</span><span class="p">)))</span> +<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>The Common Lisp <code>#'x</code> reader abbreviation is equivalent to <code>(function x)</code>, and <code>function</code> is a special form that references a value in the function namespace.</p><p>While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.</p><p>However, Hackett is a little different from all of the aforementioned languages because Hackett has <em>types</em>. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is <em>always</em> syntactically unambiguous.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Even if types and values live in separate namespaces, there is no need for a <code>type</code> form a la CL’s <code>function</code> because it can always be determined implicitly.</p><p>For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span></code></pre><p>This defines a binding named <code>Tuple</code> at the type level, which is a <em>type constructor</em> of two arguments that produces a type of kind <code>*</code>,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> and another binding named <code>Tuple</code> at the value level, which is a <em>value constructor</em> of two arguments that produces a value of type <code>(Tuple a b)</code>.</p><p>But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor <code>tuple</code> instead of <code>Tuple</code>? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>This works fine. But what happens if the programmer decides to change the name of the <code>bar</code> value?</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">qux</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>Can you spot the bug? Disturbingly, this code <em>still compiles</em>! Even though <code>bar</code> is not a member of <code>Foo</code> anymore, it’s still a valid pattern, since names used as patterns match anything, just as the <code>y</code> pattern matches against any integer inside the <code>baz</code> constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: <code>(foo-&gt;integer (baz 42))</code> will still produce <code>0</code>, not <code>42</code>, since the first case always matches.</p><p>Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the <code>Tuple</code> example above, which is illegal if a programming language only supports a single namespace.</p><p>Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The <code>Tuple</code> example from above is perfectly legal Hackett.</p><h2><a name="adding-namespaces-to-a-language"></a>Adding namespaces to a language</h2><p>Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to <em>implement</em> such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?</p><p>Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: <strong>the remainder of this blog post is <em>highly technical</em>, and some of it involves some of the more esoteric corners of Racket’s macro system</strong>. This blog post is <em>not</em> representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.</p><h3><a name="namespaces-as-scopes"></a>Namespaces as scopes</h3><p>With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as <a href="https://www.cs.utah.edu/plt/scope-sets/"><em>sets of scopes</em></a>, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.</p><p>Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the <em>value scope</em>, and all identifiers in type position must have a different scope, which we will call the <em>type scope</em>. How do we create these scopes and apply them to identifiers? In Racket, we use a function called <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29"><code>make-syntax-introducer</code></a>, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can <em>add</em> the scope to all pieces of the syntax object, <em>remove</em> the scope, or <em>flip</em> the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call <code>make-syntax-introducer</code> once for each namespace:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>We define these in a <code>begin-for-syntax</code> block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Each of these two forms is like <code>begin</code>, which is a Racket form that is, for our purposes, essentially a no-op, but it applies <code>value-introducer</code> or <code>type-introducer</code> to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">value-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">type-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span></code></pre><p>This program produces the following output:</p><pre><code>'value-x +'type-x +</code></pre><p>It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.</p><p>However, although this is close, it isn’t <em>quite</em> right. What happens if we nest the two inside each other?</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span></code></pre><pre><code>x: identifier's binding is ambiguous + context...: + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) + #(190359 intdef) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) +</code></pre><p>Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of <code>x</code> is <em>ambiguous</em> because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of <code>begin/value</code> or <code>begin/type</code> to <em>override</em> outer ones, ensuring that a use can only be in a single namespace at a time.</p><p>To do this, we simply need to adjust <code>begin/value</code> and <code>begin/type</code> to remove the other scope in addition to adding the appropriate one:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Now our nested program runs, and it produces <code>'type-x</code>, which is exactly what we want—the “nearest” scope wins.</p><p>With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.</p><h3><a name="namespaces-that-cross-module-boundaries"></a>Namespaces that cross module boundaries</h3><p>The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.</p><p>Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the <em>other</em> module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export <em>symbols</em>, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named <code>foo</code>, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the <code>require</code> form’s job to attach the correct scopes to imported identifiers.</p><p>This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup> This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary <em>points</em> of having multiple namespaces.</p><p>What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s <code>require</code> and <code>provide</code> forms are extensible, it’s even possible to implement this mangling in a completely invisible way.</p><p>Currently, the scheme that Hackett uses is to prefix <code>#%hackett-type:</code> onto the beginning of any type exports. This can be defined in terms of a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29"><em>provide pre-transformer</em></a>, which is essentially a macro that cooperates with Racket’s <code>provide</code> form to control the export process. In this case, we can define our <code>type-out</code> provide pre-transformer in terms of <a href="https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29"><code>prefix-out</code></a>, a form built-in to Racket that allows prefixing the names of exports:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">type-out</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-provide-pre-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="w"> </span><span class="n">modes</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">pre-expand-export</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">prefix-out</span><span class="w"> </span><span class="n">#%hackett-type:</span> +<span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">type-introducer</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-out</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span> +<span class="w"> </span><span class="n">modes</span><span class="p">)]))))</span></code></pre><p>Note that we call <code>type-introducer</code> in this macro! That’s because we want to ensure that, when a user writes <code>(provide (type-out Foo))</code>, we look for <code>Foo</code> in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that <code>provide</code> knows <em>which</em> <code>Foo</code> is being provided.</p><p>Once we have referenced the correct binding, the use of <code>prefix-out</code> will appropriately add the <code>#%hackett-type:</code> prefix, so the exporting side is already done. Users do need to explicitly write <code>(type-out ....)</code> if they are exporting a particular type-level binding, but this is rarely necessary, since most users use <code>data</code> or <code>class</code> to export datatypes or typeclasses respectively, which can be modified to use <code>type-out</code> internally. Very little user code actually needs to change to support this adjustment.</p><p>Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the <code>#%hackett-type:</code> prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.</p><p>Just as we extended <code>provide</code> with a provide pre-transformer, we can extend <code>require</code> using a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29"><em>require transformer</em></a>. In code, this entire process looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">and~&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">regexp-match</span><span class="w"> </span><span class="sr">#rx"^#%hackett-type:(.+)$"</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="nb">second</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">unmangle-types-in</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-require-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">imports</span><span class="w"> </span><span class="n">sources</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">expand-import</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-in</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">match-lambda</span> +<span class="w"> </span><span class="p">[(</span><span class="k">and</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="n">local-id</span><span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">local-name</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol-&gt;string</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">local-id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">unmangled-type-name</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">local-name</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">unmangled-type-name</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">unmangled-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;symbol</span><span class="w"> </span><span class="n">unmangled-type-name</span><span class="p">)</span> +<span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="n">local-id</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">unmangled-id</span><span class="p">)</span> +<span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="n">i</span><span class="p">))])</span> +<span class="w"> </span><span class="n">imports</span><span class="p">)</span> +<span class="w"> </span><span class="n">sources</span><span class="p">)])))</span></code></pre><p>This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:</p><ol><li><p>It iterates over each import and calls <code>unmangle-type-name</code> on the imported symbol. If the result is <code>#f</code>, that means the import does not have the <code>#%hackett-type:</code> prefix, and it can be safely passed through unchanged.</p></li><li><p>If <code>unmangle-type-name</code> does <em>not</em> return <code>#f</code>, then it returns the unprefixed name, which is then provided to <code>datum-&gt;syntax</code>, which allows users to forge new identifiers in an <em>unhygienic</em> (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from <code>unmangle-type-name</code>, but with the lexical context of the original identifier.</p></li><li><p>Finally, we pass the new identifier to <code>type-introducer</code> to properly add the type scope, injecting the fresh binding into the type namespace.</p></li></ol><p>With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write <code>type-out</code> when exporting types, it is unlikely that users will want to write <code>unmangle-types-in</code> around each and every import in their program. For that reason, we can define a slightly modified version of <code>require</code> that implicitly wraps all of its subforms with <code>unmangle-types-in</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="p">(</span><span class="k">rename-out</span><span class="w"> </span><span class="p">[</span><span class="n">require/unmangle</span><span class="w"> </span><span class="k">require</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">require/unmangle</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-types-in</span><span class="w"> </span><span class="n">require-spec</span><span class="p">)</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>…and we’re done. Now, Hackett modules can properly import and export type-level bindings.</p><h3><a name="namespaces-plus-submodules-the-devil-s-in-the-details"></a>Namespaces plus submodules: the devil’s in the details</h3><p>Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of <em>submodules</em>.</p><p>Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.</p><p>Submodules in Racket come in two flavors: <code>module</code> and <code>module*</code>. The difference is what order, semantically, they are defined in. Submodules defined with <code>module</code> are essentially defined <em>before</em> their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with <code>module*</code> are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.</p><p>How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.</p><p>However, there is <a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29">a special sort of <code>module*</code> submodule that uses <code>#f</code> in place of a module language</a>, which gives a module access to <em>all</em> of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a <code>test</code> submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="c1">; not provided</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span> + +<span class="p">(</span><span class="k">module*</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">rackunit</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">check-equal?</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="mi">41</span><span class="p">)</span><span class="w"> </span><span class="mi">42</span><span class="p">))</span></code></pre><p>It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>Remember that <code>make-syntax-introducer</code> is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module <a href="https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29">instantiation</a>, as ensured by Racket’s <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">separate compilation guarantee</a>. This means that each module gets its <em>own</em> pair of scopes. This means the body of a <code>module*</code> submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.</p><p>Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we <em>can</em> preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29"><code>make-syntax-delta-introducer</code></a>, we can create a syntax introducer the adds or removes the <em>difference</em> between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for <code>value-introducer</code> and <code>type-introducer</code> to always operate on the same scopes on each module instantiation:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-value/type-introducers</span> +<span class="w"> </span><span class="n">value-introducer:id</span><span class="w"> </span><span class="n">type-introducer:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">scopeless-id</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">introducer-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">value-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">type-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">type-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-value/type-introducers</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="n">type-introducer</span><span class="p">)</span></code></pre><p>The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. <em>However</em>, code inside <code>begin-for-syntax</code> blocks is still re-evaluated every time the module is instantiated! This means we are <em>not</em> circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.</p><p>We still use <code>make-syntax-introducer</code> to create our two scopes, but critically, we only call <code>make-syntax-introducer</code> inside the <code>define-value/type-introducers</code> macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds <code>value-id</code> and <code>type-id</code> as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use <code>make-syntax-delta-introducer</code> to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.</p><p>By guaranteeing each namespace’s scope is always the same, even for different modules, <code>module*</code> submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.</p><h3><a name="the-final-stretch-making-scribble-documentation-namespace-aware"></a>The final stretch: making Scribble documentation namespace-aware</h3><p>As discussed in <a href="/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/">my previous blog post</a>, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.</p><p>In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!</p><p>Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.</p><p>To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with <code>t:</code> upon import, and they are bound to Scribble <a href="https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29"><em>element transformers</em></a> that indicate they should be typeset without the <code>t:</code> prefix. Fortunately, Scribble’s documentation forms <em>do</em> understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.</p><p>In practice, this means Hackett documentation must now include a proliferation of <code>t:</code> prefixes. For example, here is the code for a typeset REPL interaction:</p><pre><code class="pygments"><span class="n">@</span><span class="p">(</span><span class="n">hackett-examples</span> +<span class="w"> </span><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">square</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">t:-&gt;</span><span class="w"> </span><span class="n">t:Integer</span><span class="w"> </span><span class="n">t:Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">[[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="p">}])</span> +<span class="w"> </span><span class="p">(</span><span class="n">square</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span></code></pre><p>Note the use of <code>t:-&gt;</code> and <code>t:Integer</code> instead of <code>-&gt;</code> and <code>Integer</code>. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.</p><p>This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides <code>deftype</code> and <code>deftycon</code> forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to <code>t:</code>-prefixed identifiers to properly index documented forms. Similarly, <code>defdata</code> and <code>defclass</code> have been updated with an understanding of types.</p><p>The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of <code>defform</code> slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into <code>t:</code>-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.</p><h2><a name="analysis-and-unsolved-problems"></a>Analysis and unsolved problems</h2><p>When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).</p><p>For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom <code>require</code> form, types provided by a module’s <em>language</em> are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike <code>require</code>’s require transformers.</p><p>To circumvent this restriction, <code>#lang hackett</code>’s reader includes a somewhat ad-hoc solution that actually inserts a <code>require</code> into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.</p><p>That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do <em>any</em> special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully <a href="http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html">internalizes extra-linguistic mechanisms</a>.</p><p>As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:</p><ul><li><p><strong>Multi-parameter typeclasses</strong> are implemented, along with <strong>default typeclass method implementations</strong>.</p></li><li><p>Pattern-matching performs basic <strong>exhaustiveness checking</strong>, so unmatched cases are a compile-time error.</p></li><li><p>Hackett ships with a <strong>larger standard library</strong>, including an <code>Either</code> type and appropriate functions, an <code>Identity</code> type, a <code>MonadTrans</code> typeclass, and the <code>ReaderT</code> and <code>ErrorT</code> monad transformers.</p></li><li><p><strong>More things are documented</strong>, and parts of the documentation are slightly improved. Additionally, <strong>Hackett’s internals are much more heavily commented</strong>, hopefully making the project more accessible to new contributors.</p></li><li><p><strong>Parts of the typechecker are dramatically simplified</strong>, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.</p></li><li><p>As always, various bug fixes.</p></li></ul><p>Finally, special mention to two new contributors to Hackett, <a href="https://github.com/iitalics">Milo Turner</a> and <a href="https://github.com/Shamrock-Frost">Brendan Murphy</a>. Also special thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> and <a href="https://github.com/michaelballantyne">Michael Ballantyne</a> for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.</p><ol class="footnotes"><li id="footnote-1"><p>“But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have <code>#lang</code>. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate <code>#lang</code>, not a modified version of Hackett, so Hackett can optimize its user experience for what it <em>is</em>, not what it might be someday. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow <code>*</code> to be used to mean “type” at the kind level and “multiply” at the value level. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, <em>per phase</em>, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Realizing Hackett, a metaprogrammable Haskellhttps://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/https://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/27 May 2017<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questionshttps://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/https://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/05 Jan 2017<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheseshttps://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/https://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/02 Jan 2017<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Climbing the infinite ladder of abstractionhttps://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/https://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/11 Aug 2016<article><p>I started programming in elementary school.</p><p>When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to <a href="https://xkcd.com/974/">solve the general problem</a>. When I learned about programming, I was immediately hooked: it was <em>so easy</em> to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.</p><p>Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of <strong>abstraction</strong>.</p><h2><a name="the-brick-wall-of-inexpressiveness"></a>The brick wall of inexpressiveness</h2><p>When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:</p><pre><code class="pygments"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="p">;</span> +<span class="p">}</span> + +<span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span> +<span class="p">}</span></code></pre><p>This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.</p><p>It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The <em>whole reason</em> I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.</p><p>When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.</p><p>Both of these languages were flawed, as all languages are, but they opened my mind to the idea that <em>programming languages</em> could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.</p><h2><a name="discovering-lisp"></a>Discovering Lisp</h2><p>Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.</p><p>This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?</p><p>The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a <a href="https://github.com/lexi-lambda/libsol">highly dynamic Lisp with a prototypal object system called Sol</a>. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.</p><p>Unfortunately, it was also abysmally slow.</p><p>I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called <a href="http://racket-lang.org">Racket</a>. At about the same time, someone pointed me to a totally different language called <a href="https://www.haskell.org">Haskell</a>. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.</p><h2><a name="a-journey-into-complexity"></a>A journey into complexity</h2><p>Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned <em>so much more</em> since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.</p><p>I’ve also started realizing something else, though: <strong>the languages I’ve settled into are <em>really complicated</em>.</strong></p><p>When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.</p><p>Why?</p><p>Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”</p><p>Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?</p><p>I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.</p><p>Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I <em>still</em> have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?</p><h3><a name="improvement-but-never-mastery"></a>Improvement, but never mastery</h3><p>Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.</p><p>For example I am pretty confident that I <em>get</em> JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques <em>within</em> the language, not entirely new linguistic constructs.</p><p>On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about <code>TypeInType</code>? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do <em>not</em> have a good grasp on all the intricacies of Racket’s macro system.</p><p>This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out <code>DataKinds</code>, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.</p><p>Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is <em>terrible</em>, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.</p><p>Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even <em>more</em> advanced things, and still, none of them truly solve my problems.</p><h2><a name="ultimately-it-all-has-at-least-a-little-value"></a>Ultimately, it all has (at least a little) value</h2><p>It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.</p><p>The scariest part of all is that I think it’s completely worthwhile.</p><p>So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs <em>seem</em> unnecessarily complicated, they <em>seem</em> increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.</p><p>The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.</p><p>Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:</p><pre><code class="pygments"><span class="nl">10</span><span class="w"> </span><span class="kr">INPUT</span><span class="w"> </span><span class="s2">"What is your name: "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span> +<span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">"Hello "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span></code></pre><p>On the other hand, very few would probably understand this one:</p><pre><code class="pygments"><span class="c1">-- | A class for categories.</span> +<span class="c1">-- id and (.) must form a monoid.</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">Category</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="c1">-- | the identity morphism</span> +<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="c1">-- | morphism composition</span> +<span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">c</span></code></pre><p>Yet very few new programs are being written in BASIC, and lots are being written in Haskell.</p><p>Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:</p><pre><code class="pygments"><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composeWithProps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">curry</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">childProps</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">omit</span><span class="p">([</span><span class="s1">&#39;children&#39;</span><span class="p">],</span><span class="w"> </span><span class="nx">childProps</span><span class="p">),</span><span class="w"> </span><span class="nx">childProps</span><span class="p">.</span><span class="nx">children</span><span class="p">));</span> +<span class="w"> </span><span class="c1">// give the composed component a pretty display name for debugging</span> +<span class="w"> </span><span class="nx">composed</span><span class="p">.</span><span class="nx">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`Composed(</span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="si">}</span><span class="sb">, </span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">composed</span><span class="p">;</span> +<span class="p">});</span></code></pre><p>Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.</p><p>That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an <em>extremely</em> productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.</p><h3><a name="reactionary-anti-intellectualism-and-the-search-for-moderation"></a>Reactionary anti-intellectualism and the search for moderation</h3><p>I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. <a href="https://twitter.com/lexi_lambda/status/763111451691134976">I recently tweeted a picture of some rather dense mathematics from a paper I’d read</a>, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.</p><p>Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like <code>map</code> should be banned because they are too confusing and not immediately self-explanatory.</p><p>I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.</p><p>Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern <em>technology</em> possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.</p><p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.</p><p>I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.</p><p>Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/racket.atom.xml b/feeds/racket.atom.xml new file mode 100644 index 0000000..e9278ad --- /dev/null +++ b/feeds/racket.atom.xml @@ -0,0 +1,1691 @@ +Posts tagged ‘racket’ | Alexis King’s Blog2019-04-21T00:00:00ZDefeating Racket’s separate compilation guarantee2019-04-21T00:00:00Z2019-04-21T00:00:00ZAlexis King<article><p>Being a self-described <a href="https://felleisen.org/matthias/manifesto/sec_pl-pl.html">programming-language programming language</a> is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate <a href="https://www.cs.utah.edu/plt/publications/macromod.pdf">composable and compilable macros</a>. One of the module system’s foundational properties is its <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29"><em>separate compilation guarantee</em></a>, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is <em>essential</em> for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.</p><p>Yet today, in this blog post, we’re going to do exactly that.</p><h2><a name="what-is-the-separate-compilation-guarantee"></a>What is the separate compilation guarantee?</h2><p>Before we get to the fun part (i.e. breaking things), let’s go over some fundamentals so we understand what we’re breaking. The authoritative source for the separate compilation guarantee is <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">the Racket reference</a>, but it is dense, as authoritative sources tend to be. Although I enjoy reading technical manuals for sport, it is my understanding that not all the people who read this blog are as strange as I am, so let’s start with a quick primer, instead. (If you’re already an expert, feel free to <a href="#section:main-start">skip to the next section</a>.)</p><p>Racket is a macro-enabled programming language. In Racket, a macro is a user-defined, code-to-code transformation that occurs at compile-time. These transformations cannot make arbitrary changes to the program—in Racket, they are usually required to be <em>local</em>, affecting a single expression or definition at a time—but they may be implemented using arbitrary code. This means that a macro can, if it so desires, read the SSH keys off your filesystem and issue an HTTP request to send them someplace.</p><p>That kind of attack is bad, admittedly, but it’s also <em>uninteresting</em>: Racket allows you do all that and then some, making no attempt to prevent it.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Racket calls these “external effects,” things that affect state outside of the programming language. They sound scary, but in practice, <em>internal effects</em>—effects that mutate state inside the programming language—are a much bigger obstacle to practical programming. Let’s take a look at why.</p><p>Let’s say we have a module with some global, mutable state. Perhaps it is used to keep track of a set of delicious foods:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><p>Using this interface, let’s write a program that checks if a particular food, given as a command-line argument, is delicious:</p><pre><code class="pygments"><span class="c1">;; check-food.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"pineapple"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"sushi"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"cheesecake"</span><span class="p">)</span> + +<span class="p">(</span><span class="k">command-line</span> +<span class="w"> </span><span class="kd">#:args</span><span class="w"> </span><span class="p">[</span><span class="n">food-to-check</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is not delicious.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>cheesecake +cheesecake<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>licorice +licorice<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>Exhilarating. (Sorry, licorice fans.) But what if a <em>macro</em> were to call <code>add-delicious-food!</code>? What would happen? For example, what if we wrote a macro to add a lot of foods at once?<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="n">fst:string</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd:string</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">for*</span><span class="w"> </span><span class="p">([</span><span class="n">fst-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">fst</span><span class="w"> </span><span class="k">...</span><span class="p">]))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">snd</span><span class="w"> </span><span class="k">...</span><span class="p">]))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="p">(</span><span class="nb">string-append</span><span class="w"> </span><span class="n">fst-str</span><span class="w"> </span><span class="s2">" "</span><span class="w"> </span><span class="n">snd-str</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">))</span> + +<span class="c1">; should add “fried chicken,” “roasted chicken”, “fried potato,” and “roasted potato”</span> +<span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="s2">"fried"</span><span class="w"> </span><span class="s2">"roasted"</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="s2">"chicken"</span><span class="w"> </span><span class="s2">"potato"</span><span class="p">])</span></code></pre><p>Now, what do you think executing <code>racket check-food.rkt 'fried chicken'</code> will do?</p><p>Clearly, the program should print <code>fried chicken is a delicious food</code>, and indeed, many traditional Lisp systems would happily produce such a result. After all, running <code>racket check-food.rkt 'fried chicken'</code> must load the source code inside <code>check-food.rkt</code>, expand and compile it, then run the result. While the program is being expanded, the compile-time calls to <code>add-delicious-food!</code> should add new elements to the <code>delicious-food</code> set, so when the program is executed, the string <code>"fried chicken"</code> ought to be in it.</p><p>But if you actually try this yourself, you will find that <em>isn’t</em> what happens. Instead, Racket rejects the program:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +check-food.rkt:12:11:<span class="w"> </span>add-delicious-food!:<span class="w"> </span>reference<span class="w"> </span>to<span class="w"> </span>an<span class="w"> </span>unbound<span class="w"> </span>identifier +<span class="w"> </span>at<span class="w"> </span>phase:<span class="w"> </span><span class="m">1</span><span class="p">;</span><span class="w"> </span>the<span class="w"> </span>transformer<span class="w"> </span>environment +<span class="w"> </span><span class="k">in</span>:<span class="w"> </span>add-delicious-food!</code></pre><p>Why does Racket reject this program? Well, consider that Racket allows programs to be pre-compiled using <code>raco make</code>, doing all the work of macroexpansion and compilation to bytecode ahead of time. Subsequent runs of the program will use the pre-compiled version, without having to run all the macros again. This is a problem, since expanding the <code>add-food-combinations!</code> macro had side-effects that our program depended on!</p><p>If Racket allowed the above program, it might do different things depending on whether it was pre-compiled. Running directly from source code might treat <code>'fried chicken'</code> as a delicious food, while running from pre-compiled bytecode might not. Racket considers this unacceptable, so it disallows the program entirely.</p><h3><a name="preserving-separate-compilation-via-phases"></a>Preserving separate compilation via phases</h3><p>Hopefully, you are now mostly convinced that the above program is a bad one, but you might have some lingering doubts. You might, for example, wonder if Racket disallows mutable compile-time state entirely. That is not the case—Racket really does allow everything that happens at runtime to happen at compile-time—but it does prevent compile-time and run-time state from ever <em>interacting</em>. Racket stratifies every program into a compile-time part and a run-time part, and it restricts communication between them to limited, well-defined channels (mainly via expanding to code that does something at run-time).</p><p>Racket calls this system of stratification <em>phases</em>. Code that executes at run-time belongs to the <em>run-time phase</em>, while code that executes at compile-time (i.e. macros) belongs to the <em>compile-time phase</em>. When a variable is defined, it is always defined in a particular phase, so bindings declared with <code>define</code> can only be used at run-time, while bindings declared with <code>define-for-syntax</code> can only be used at compile-time. Since <code>add-delicious-food!</code> was declared using <code>define</code>, it was not allowed (and in fact was not even visible) in the body of the <code>add-food-combinations!</code> macro.</p><p>While the whole macro system could work precisely as just described, such a strict stratification would be incredibly rigid. Since every definition would belong to either run-time or compile-time, but never both, reusing run-time code to implement macros would be impossible. While the example in the previous section might make it seem like that’s a good thing, it very often isn’t: imagine if general-purpose functions like <code>map</code> and <code>filter</code> all needed to be written twice!</p><p>To avoid this problem, Racket allows modules to be imported at both run-time and compile-time, so long as it’s done explicitly. Writing <code>(require "some-library.rkt")</code> requires <code>some-library.rkt</code> for run-time code, but writing <code>(require (for-syntax "some-library.rkt"))</code> requires it for compile-time code. Requiring a module <code>for-syntax</code> is sort of like implicitly adjusting all of its uses of <code>define</code> to be <code>define-for-syntax</code>, instead, effectively shifting all the code from run-time to compile-time. This kind of operation is therefore known as <em>phase shifting</em> in Racket terminology.</p><p>We can use phase shifting to make the program we wrote compile. If we adjust the <code>require</code> at the beginning of our program, then we can ensure <code>add-delicious-food!</code> is visible to both the run-time and compile-time parts of <code>check-food.rkt</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">))</span></code></pre><p>Now our program compiles. However, if you’ve been following everything carefully, you should be wondering why! According to the last section, sharing state between run-time and compile-time fundamentally can’t work without introducing inconsistencies between uncompiled and pre-compiled code. And that’s true—such a thing would cause all sorts of problems, and Racket doesn’t allow it. If you run the program, whether pre-compiled or not, you’ll find it always does the same thing:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>This seems rather confusing. What happened to the calls to <code>add-delicious-food!</code> inside our <code>add-food-combinations!</code> macro? If we stick a <code>printf</code> inside <code>add-delicious-food!</code>, we’ll find that it really does get called:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"Registering ~a as a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And in fact, if we pre-compile <code>check-food.rkt</code>, we’ll see that the first four registrations appear at compile-time, exactly as we expect:</p><pre><code class="pygments">$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>The compile-time registrations really are happening, but Racket is automatically restricting the compile-time side-effects so they only apply at compile-time. After compilation has finished, Racket ensures that compile-time side effects are thrown away, and the run-time code starts over with fresh, untouched state. This guarantees consistent behavior, since it becomes impossible to distinguish at run-time whether a module was just compiled on the fly, or if it was pre-compiled long ago (possibly even on someone else’s computer).</p><p>This is the essence of the separate compilation guarantee. To summarize:</p><ul><li><p>Run-time and compile-time are distinct <em>phases</em> of execution, which cannot interact.</p></li><li><p>Modules can be required at multiple phases via <em>phase shifting</em>, but their state is kept separate. Each phase gets its own copy of the state.</p></li><li><p>Ensuring that the state is kept separate ensures predictable program behavior, no matter when the program is compiled.</p></li></ul><p>This summary is a simplification of phases in Racket. The full Racket module system does not have only two phases, since macros can also be <em>used</em> at compile-time to implement other macros, effectively creating a separate “compile-time” for the compile-time code. Each compile-time pass is isolated to its own phase, creating a finite but arbitrarily large number of distinct program phases (all but one of which occur at compile-time).</p><p>Furthermore, the separate compilation guarantee does not just isolate the state of each phase from the state of other phases but also isolates all compile-time state from the compile-time state of other modules. This ensures that compilation is still deterministic even if modules are compiled in a different <em>order</em>, or if several modules are sometimes compiled individually while other times compiled together all at once.</p><p>If you want to learn more, the full details of the module system are described at length in the <a href="https://docs.racket-lang.org/guide/phases.html">General Phase Levels</a> section of the Racket Guide, but the abridged summary I’ve described is enough for the purposes of this blog post. If the bulleted list above mostly made sense to you, you’re ready to move on.</p><h2 id="section:main-start">How we’re going to break it</h2><p>The separate compilation guarantee is a sturdy opponent, but it is not without weaknesses. Although no API in Racket, safe or unsafe, allows arbitrarily disabling phase separation, a couple features of Racket are already known to allow limited forms of cross-phase communication.</p><p>The most significant of these, and the one we’ll be using as our vector of attack, is the logger. Unlike many logging systems, which are exclusively string-oriented, Racket’s logging interface allows structured logging by associating an arbitrary Racket value with each and every log message. Since it is possible to set up listeners within Racket that receive log messages sent to a particular “topic,” the logger can be used as a communication channel to send values between different parts of a program.</p><p>The following program illustrates how this works. One thread creates a listener for all log messages on the topic <code>'send-me-a-value</code> using <code>make-log-receiver</code>, then uses <code>sync</code> to block until a value is received. Meanwhile, a second thread sends values through the logger using <code>log-message</code>. Together, this creates a makeshift buffered, asynchronous channel:</p><pre><code class="pygments"><span class="c1">;; log-comm.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span><span class="w"> </span><span class="c1">; wait forever</span></code></pre><pre><code>$ racket log-comm.rkt +'#(debug "" 1 send-me-a-value) +'#(debug "" 2 send-me-a-value) +'#(debug "" 3 send-me-a-value) +'#(debug "" 4 send-me-a-value) +^Cuser break +</code></pre><p>In this program, the value being sent through the logger is just a number, which isn’t very interesting. But the value really can be <em>any</em> value, even arbitrary closures or mutable data structures. It’s even possible to send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> through a logger, which can subsequently be used to communicate directly, without having to abuse the logger.</p><p>Generally, this feature of loggers isn’t very useful, since Racket has plenty of features for cross-thread communication. What’s special about the logger, however, is that it is global, and it is cross-phase.</p><p>The cross-phase nature of the logger makes some sense. If a Racket program creates a namespace (that is, a fresh environment for dynamic evaluation), then uses it to expand and compile a Racket module, the process of compilation might produce some log messages, and the calling thread might wish to receive them. It wouldn’t be a very useful logging system if log messages during compile-time were always lost. However, this convenience is a loophole in the phase separation system, since it allows values to flow—bidirectionally—between phases.</p><p>This concept forms the foundation of our exploit, but it alone is not a new technique, and I did not discover it. However, all existing uses I know of that use the logger for cross-phase communication require control of the parent namespace in which modules are being compiled, which means some code must exist “outside” the actual program. That technique does not work for ordinary programs run directly with <code>racket</code> or compiled directly with <code>raco make</code>, so to get there, we’ll need something more clever.</p><h3><a name="the-challenge"></a>The challenge</h3><p>Our goal, therefore, is to share state between phases <em>without</em> controlling the compilation namespace. More precisely, we want to be able to create an arbitrary module-level definition that is <em>cross-phase persistent</em>, which means it will be evaluated once and only once no matter how many times its enclosing module is re-instantiated (i.e. given a fresh, untouched state) at various phases. A phase-shifted <code>require</code> of the module that contains the definition should share state with an unshifted version of the module, breaking the separate compilation guarantee wide open.</p><p>To use the example from the previous section, we should be able to adjust <code>foods.rkt</code> very slightly…</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="c1">; share across phases</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="cm">#| ... |#</span></code></pre><p>…and the <code>delicious-foods</code> mutable state should magically become cross-phase persistent. When running <code>check-food.rkt</code> from source, we should see the side-effects persisted from the module’s compilation, while running from pre-compiled bytecode should give us the result with compile-time effects discarded.</p><p>We already know the logger is going to be part of our exploit, but implementing <code>define/cross-phase</code> on top of it is more subtle than it might seem. In our previous example that used <code>make-log-receiver</code>, we had well-defined sender and receiver threads, but who is the “sender” in our multi-phased world? And what exactly is the sender sending?</p><p>To answer those questions, allow me to outline the general idea of our approach:</p><ol><li><p>The first time our <code>foods.rkt</code> module is instantiated, at any phase, it evaluates the <code>(mutable-set)</code> expression to produce a new mutable set. It spawns a sender thread that sends this value via the logger to anyone who will listen, and that thread lingers in the background for the remaining duration of the program.</p></li><li><p>All subsequent instantiations of <code>foods.rkt</code> do <em>not</em> evaluate the <code>(mutable-set)</code> expression. Instead, they obtain the existing set by creating a log receiver and obtaining the value the sender thread is broadcasting. This ensures that a single value is shared across all instantiations of the module.</p></li></ol><p>This sounds deceptively simple, but the crux of the problem is how to determine whether <code>foods.rkt</code> has previously been instantiated or not. Since we can only communicate across phases via the logger, we cannot use any shared state to directly record the first time the module is instantiated. We can listen to a log receiver and wait to see if we get a response, but this introduces a race condition: how long do we wait until giving up and deciding we’re the first instantiation? Worse, what if two threads instantiate the module at the same time, and both threads end up spawning a new sender thread, duplicating the state?</p><p>The true challenge, therefore, is to develop a protocol by which we can be <em>certain</em> we are the first instantiation of a module, without relying on any unspecified behavior, and without introducing any race conditions. This is possible, but it isn’t obvious, and it requires combining loggers with some extra tools available to the Racket programmer.</p><h3><a name="the-key-idea"></a>The key idea</h3><p>It’s finally time to tackle the key idea at the heart of our exploit: garbage collection. In Racket, garbage collection is an observable effect, since Racket allows attaching finalizers to values via <a href="https://docs.racket-lang.org/reference/willexecutor.html">wills and executors</a>. Since a single heap is necessarily shared by the entire VM, behavior happening on other threads (even in other phases) can be indirectly observed by creating a unique value—a “canary”—then sending it to another thread, and waiting to see if it will be garbage collected or not (that is, whether or not the canary “dies”).</p><p>Remember that logs and log receivers are effectively buffered, multicast, asynchronous FIFO channels. Since they are buffered, if any thread is already listening to a logger topic when a value is sent, it cannot possibly be garbage collected until that thread either reads it and discards it or the receiver itself is garbage collected. It’s possible to use this mechanism to observe whether or not another thread is already listening on a topic, as the following program demonstrates:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">executor</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><pre><code>$ racket check-receivers.rkt +no receivers for 'bar +receiver exists for 'foo +</code></pre><p>However, this program has some problems. For one, it needs to call <code>collect-garbage</code> several times to be certain that the canary will be collected if there are no listeners, which can take a second or two, and it also assumes that three calls to <code>collect-garbage</code> will be enough to collect the canary, though there is no guarantee that will be true.</p><p>A bulletproof solution should be both reasonably performant and guaranteed to work. To get there, we have to combine this idea with something more. Here’s the trick: instead of sending the canary alone, send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> alongside it. Synchronize on both the canary’s executor <em>and</em> the channel so that the thread will unblock if either the canary is collected <em>or</em> the channel is received and sent a value using <code>channel-put</code>. Have the receiver listen for the channel on a separate thread, and when it receives it, send a value back to unblock the waiting thread as quickly as possible, without needing to rely on a timeout or a particular number of calls to <code>collect-garbage</code>.</p><p>Using that idea, we can revise the program:</p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="c1">; send the channel + the canary</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">canary</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span><span class="w"> </span><span class="c1">; yield to try to let the receiver thread work</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">received</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">)))</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">received</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span><span class="w"> </span><span class="c1">; collect garbage and try again</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> +<span class="p">(</span><span class="nb">void</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="k">_</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><p>Now the program completes almost instantly. For this simple program, the explicit <code>(sleep)</code> call is effective enough at yielding that, on my machine, <code>(check-receivers 'foo)</code> returns without ever calling <code>collect-garbage</code>, and <code>(check-receivers 'bar)</code> returns after performing a single minor collection.</p><p>This is extremely close to a bulletproof solution, but there are two remaining subtle issues:</p><ol><li><p>There is technically a race condition between the <code>(sync recv)</code> in the receiver thread and the subsequent <code>channel-put</code>, since it’s possible for the canary to be received, discarded, and garbage collected before reaching the call to <code>channel-put</code>, which the sending thread would incorrectly interpret as indicating the topic has no receivers.</p><p>To fix that, the receiver thread can send the canary itself back through the channel, which fundamentally has to work, since the value cannot be collected until it has been received by the sending thread, at which point the <code>sync</code> has already chosen the channel.</p></li><li><p>It is possible for the receiver thread to receive the log message and call <code>channel-put</code>, but for the sending thread to somehow die in the meantime (which cannot be protected against in general in Racket, since <code>thread-kill</code> immediately and forcibly terminates a thread). If this were to happen, the sending thread would never obtain the value from the channel, blocking the receiving thread indefinitely.</p><p>A solution is to spawn a new thread for each <code>channel-put</code> instead of calling it directly from the receiving thread. Conveniently, this both ensures the receiving thread never gets stuck and avoids resource leaks, since the Racket runtime is smart enough to GC a thread blocked on a channel that has no other references and therefore can never be unblocked.</p></li></ol><p>With those fixes in place, the program is, to the best of my knowledge, bulletproof. It will always correctly determine whether or not a logger has a listener, with no race conditions or reliance upon unspecified behavior of the Racket runtime. It does, however, make a couple of assumptions.</p><p>First, it assumes that the value of <code>(current-logger)</code> is shared between the threads. It is true that <code>(current-logger)</code> can be changed, and sometimes is, but it’s usually done via <code>parameterize</code>, not mutation of the parameter directly. Therefore, this can largely be mitigated by storing the value of <code>(current-logger)</code> at module instantiation time.</p><p>Second, it assumes that no other receivers are listening on the same topic. Technically, even using a unique, uninterned key for the topic is insufficient to ensure that no receivers are listening to it, since a receiver can choose to listen to all topics. However, in practice, it is highly unlikely that any receiver would willfully choose to listen to all topics at the <code>'debug</code> level, since the receiver would be inundated with enormous amounts of useless information. Even if such a receiver were to be created, it is highly likely that it would dequeue the messages as quickly as possible and discard the accompanying payload, since doing otherwise would cause all messages to be retained in memory, leading to a significant memory leak.</p><p>Both these problems can be mitigated by using a logger other than the root logger, which is easy in this example. However, for the purpose of subverting the separate compilation guarantee, we would have no way to share the logger object itself across phases, defeating the whole purpose, so we are forced to use the root logger and hope the above two assumptions remain true (but they usually do).</p><h3><a name="preparing-the-exploit"></a>Preparing the exploit</h3><p>If you’ve made it here, congratulations! The most difficult part of this blog post is over. All that’s left is the fun part: performing the exploit.</p><p>The bulk of our implementation is a slightly adapted version of <code>check-receivers</code>:</p><pre><code class="pygments"><span class="c1">;; define-cross-phase.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="k">thunk</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="k">==</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="nb">eq?</span><span class="p">))</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">execute-evt</span><span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="nb">will-execute</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">execute-evt</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">result</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">value</span><span class="p">)</span> +<span class="w"> </span><span class="n">value</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="p">(</span><span class="k">thunk</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">value</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)]))))</span> +<span class="w"> </span><span class="n">value</span><span class="p">]))</span></code></pre><p>There are a few minor differences, which I’ll list:</p><ol><li><p>The most obvious difference is that <code>make-cross-phase</code> does the work of both checking if a receiver exists—which I’ll call the <em>manager thread</em>—and spawning it if it doesn’t. If it does end up spawning a manager thread, it evaluates the given thunk to produce a value, which becomes the cross-phase value that will be sent through the channel alongside the canary.</p></li><li><p>Once the manager thread is created, subsequent calls to <code>make-cross-phase</code> will receive the value through the channel and return it instead of re-invoking <code>thunk</code>. This is what ensures the right-hand side of each use of <code>define/cross-phase</code> is only ever evaluated once.</p></li><li><p>Since <code>make-cross-phase</code> needs to create a log receiver if no manager thread exists, it does so immediately, before sending the canary through the logger. This avoids a race condition between multiple threads that are simultaneously competing to become the manager thread, where both threads could send a canary through the logger before either was listening, both canaries would get GC’d, and both threads would spawn a new manager.<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><p>Creating the receiver before sending the canary avoids this problem, but the thread now needs to receive its own canary and discard it before synchronizing on the channel and executor, since otherwise it will retain a reference to the canary. It’s possible that in between creating the receiver and sending the canary, another thread also sent a canary, so it needs to drop any log messages it finds that don’t include its own canary.</p><p>This ends up working out perfectly, since every thread drops all the messages received before the one containing its own canary, but retains all subsequent values. This means that only one thread can ever “win” and become the manager, since the first thread to send a canary is guaranteed to retain all subsequent canaries, yet also guaranteed its canary will be GC’d. Other threads racing to become the manager will remain blocked until the manager thread is created, since its canaries will be retained by the manager-to-be until it dequeues them.</p><p>(This is the most subtle part of the process to get right, but conveniently, it mostly just works out without very much code. If you didn’t understand any of the above three paragraphs, it isn’t a big deal.)</p></li></ol><p>The final piece to this puzzle is to define the <code>define/cross-phase</code> macro that wraps <code>make-cross-phase</code>. The macro is actually slightly more involved than just generating a call to <code>make-cross-phase</code> directly, since we’d like to use an uninterned symbol for the topic instead of an interned one, just to ensure it is unique. Ordinarily, this might seem impossible, since an uninterned symbol is fundamentally a unique value that needs to be communicated across phases, and the whole problem we are solving is creating a communication channel that spans phases. However, Racket actually provides some built-in support for sharing uninterned symbols across phases (plus some other kinds of values, but they must always be immutable). To do this, we need to generate a <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._cross-phase._persistent-modules%29">cross-phase persistent submodule</a> that exports an uninterned symbol, then pass that symbol as the topic to <code>make-cross-phase</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">define/cross-phase</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">cross-phase-topic-key</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">#%kernel</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%declare</span><span class="w"> </span><span class="kd">#:cross-phase-persistent</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%provide</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">topic</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="s2">"cross-phase"</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">topic-mod-name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">e</span><span class="p">)))))</span></code></pre><p>And that’s really it. We’re done.</p><h3><a name="executing-the-exploit"></a>Executing the exploit</h3><p>With our implementation of <code>define/cross-phase</code> in hand, all that’s left to do is run our original <code>check-foods.rkt</code> program and see what happens:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +set-add!:<span class="w"> </span>contract<span class="w"> </span>violation: +expected:<span class="w"> </span>set? +given:<span class="w"> </span><span class="o">(</span>mutable-set<span class="w"> </span><span class="s2">"fried chicken"</span><span class="w"> </span><span class="s2">"roasted chicken"</span><span class="w"> </span><span class="s2">"roasted potato"</span><span class="w"> </span><span class="s2">"fried potato"</span><span class="o">)</span> +argument<span class="w"> </span>position:<span class="w"> </span>1st +other<span class="w"> </span>arguments...: +<span class="w"> </span>x:<span class="w"> </span><span class="s2">"pineapple"</span></code></pre><p>Well, I don’t know what you expected. Play stupid games, win stupid prizes.</p><p>This error actually makes sense, but it belies one reason (of many) why this whole endeavor is probably a bad idea. Although we’ve managed to make our mutable set cross-phase persistent, our references to set operations like <code>set-add!</code> and <code>set-member?</code> are not, and every time <code>racket/set</code> is instantiated in a fresh phase, it creates an entirely new instance of the <code>set</code> structure type. This means that even though we have a bona fide mutable set, it isn’t actually the type of set that this phase’s <code>set-add!</code> understands!</p><p>Of course, this isn’t a problem that some liberal application of <code>define/cross-phase</code> can’t solve:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-member?</span><span class="w"> </span><span class="nb">set-member?</span><span class="p">)</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-add!</span><span class="w"> </span><span class="nb">set-add!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And thus we find that another so-called “guarantee” isn’t.</p><h2><a name="reflection"></a>Reflection</h2><p>Now comes the time in the blog post when I have to step back and think about what I’ve done. Have mercy.</p><p>Everything in this blog post is a terrible idea. No, you should not use loggers for anything that isn’t logging, you shouldn’t use wills and executors for critical control flow, and obviously you should absolutely not intentionally break one of the most helpful guarantees the Racket module system affords you.</p><p>But I thought it was fun to do all that, anyway.</p><p>The meaningful takeaways from this blog post aren’t that the separate compilation guarantee can be broken, nor that any of the particular techniques I used hold, but that</p><ol><li><p>ensuring non-trivial guarantees is really hard,</p></li><li><p>despite that, the separate compilation guarantee is really, really hard to break,</p></li><li><p>the separate compilation guarantee is good, and you should appreciate the luxury it affords you while writing Racket macros,</p></li><li><p>avoiding races in a concurrent environment can be extremely subtle,</p></li><li><p>and Racket is totally <em>awesome</em> for giving me this much rope to hang myself with.</p></li></ol><p>If you want to hang yourself with Racket, too, <a href="https://gist.github.com/lexi-lambda/f173a84fc9727977bcea657b3bb0cd4f">runnable code from this blog post is available here</a>.</p><ol class="footnotes"><li id="footnote-1"><p>This isn’t <em>strictly</em> true, since Racket provides sandboxing mechanisms that can compile and execute untrusted code without file system or network access, but this is not the default compilation mode. Usually, it doesn’t matter nearly as much as it might sound: most of the time, if you’re compiling untrusted code, you’re also going to run it, and running untrusted code can do all those things, anyway. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>This is actually a <em>terrible</em> use case for a macro, since an ordinary function would do just fine, but I’m simplifying a little to keep the example small. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Racket actually provides this functionality directly via the <code>log-level?</code> procedure. However, since <code>log-level?</code> provides no way to determine how <em>many</em> receivers are listening to a topic, using it to guard against creating a receiver is vulnerable to a race condition that the garbage collection-based approach can avoid, as is discussed later. Furthermore, the GC technique is more likely to be resilient to nosy log receivers listening on all topics at the <code>'debug</code> level, since they will almost certainly dequeue and discard the value quickly (as otherwise they would leak large quantities of memory). <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>This race is the one that makes using <code>log-level?</code> untenable, since the receiver needs to be created before the topic is checked for listeners to avoid the race, which can’t be done with <code>log-level?</code> (since it would always return <code>#t</code>). <a href="#footnote-ref-4-1">↩</a></p></li></ol></article>Macroexpand anywhere with local-apply-transformer!2018-10-06T00:00:00Z2018-10-06T00:00:00ZAlexis King<article><p>Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only <em>certain positions</em> in Racket code are subject to macroexpansion.</p><p>To illustrate, consider a macro that provides a Clojure-style <code>let</code> form:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This can be used anywhere an expression is expected, and it does as one would expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>However, a novice macro programmer might realize that <code>clj-let</code> really only modifies the syntax of <em>binding pairs</em> for a <code>let</code> form. Therefore, could one define a macro that only adjusts the binding pairs of some existing <code>let</code> form instead of expanding to an entire <code>let</code>? That is, could one write the above example like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>The answer is <em>no</em>: the binding pairs of a <code>let</code> form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion <em>anywhere</em> in a Racket program.</p><h2><a name="why-only-some-positions-are-subject-to-macroexpansion"></a>Why only some positions are subject to macroexpansion</h2><p>To understand <em>why</em> the macroexpander refuses to touch certain positions in a program, we must first understand how the macro system operates. In Racket, a macro is defined as a compile-time function associated with a particular binding, and macros are given complete control over the syntax trees they are surrounded with. If we define a macro <em><code>mac</code></em>, then we write the expression <code>(<em>mac</em> <em>form</em>)</code>, <em><code>form</code></em> is provided as-is to <em><code>mac</code></em> as a syntax object. Its structure can be anything at all, since <em><code>mac</code></em> can be an arbitrary Racket function, and that function can use <em><code>form</code></em> however it pleases.</p><p>To give a concrete illustration, consider a macro that binds some identifiers to symbols in a local scope:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">x</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="ss">hello</span><span class="w"> </span><span class="ss">goodbye</span><span class="p">)</span></code></pre><p>It isn’t the most exciting macro in the world, but it illustrates a key point: the first subform to <code>let-symbols</code> is a list of identifiers that are eventually put in <em>binding</em> position. This means that <code>hello</code> and <code>goodbye</code> are bindings, not uses, and such bindings shadow any existing bindings that might have been in scope:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">foo</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> +<span class="w"> </span><span class="n">foo</span><span class="p">))</span> +<span class="o">&#39;</span><span class="ss">foo</span></code></pre><p>This might not seem very interesting, but it’s critical to understand, since it means that the expander <em>can’t know</em> which sub-pieces of a use of <code>let-symbols</code> will eventually be expressions themselves until it expands the macro and discovers it produces a <code>let</code> form, so it can’t know where it’s safe to perform macroexpansion. To make this more explicit, imagine we define a macro under some name, then try and use that name with our <code>let-symbols</code> macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">x:id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="n">hello</span><span class="p">)</span></code></pre><p>What should the above program do? If we treat the first use of <code>hello</code> in the <code>let-symbols</code> form as a macro application, then <code>(hello goodbye)</code> should be transformed into <code>(goodbye)</code>, and the use of <code>hello</code> in the body should be a syntax error. But if the first use of <code>hello</code> was instead intended to be a binder, then it should shadow the <code>hello</code> definition above, and the output of the program should be <code>'hello</code>.</p><p>To avoid the chaos that would ensue if defining a macro could completely break local reasoning about other macros, Racket chooses the second option, and the program produces <code>'hello</code>. The macroexpander has no way of knowing <em>how</em> each macro will inspect its constituent pieces, so it avoids touching anything until the macro expands. After it discovers the <code>let</code> form in the expansion of <code>let-symbols</code>, it can safely determine that the body expressions are, indeed, expressions, and it can recursively expand any macros they contain. To put things another way, a macro’s sub-forms are never expanded before the macro itself is expanded, only after.</p><h2><a name="forcing-sub-form-expansion"></a>Forcing sub-form expansion</h2><p>The above section explains why the expander must operate as it does, but it’s a little bit unsatisfying. What if we write a macro where we <em>want</em> certain sub-forms to be expanded before they are passed to us? Fortunately, the Racket macro system provides an API to handle this use case, too.</p><p>It is true that the Racket macro system never <em>automatically</em> expands sub-forms before outer forms are expanded, but macro transformers can explicitly op-in to recursive expansion via the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a> function. This function effectively yields control back to the expander to expand some arbitrary piece of syntax as an expression, and when it returns, the macro transformer can inspect the expanded expression however it wishes. In theory, this can be used to implement extensible macros that allow macroexpansion in locations other than expression position.</p><p>To give an example of such a macro, consider the Racket <code>match</code> form, which implements an expressive pattern-matcher as a macro. One of the most interesting qualities of Racket’s <code>match</code> macro is that its pattern language is user-extensible, essentially allowing pattern-level macros. For example, a user might find they frequently match against natural numbers, and they wish to be able to write <code>(nat n)</code> as a shorthand for <code>(? exact-nonnegative-integer? n)</code>. Fortunately, this is easy using <code>define-match-expander</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-match-expander</span><span class="w"> </span><span class="n">nat</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">pat</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">?</span><span class="w"> </span><span class="nb">exact-nonnegative-integer?</span><span class="w"> </span><span class="n">pat</span><span class="p">)]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="mi">-5</span><span class="w"> </span><span class="mi">-2</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">-7</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">list</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">nat</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">n</span><span class="p">])</span> +<span class="mi">4</span></code></pre><p>Clearly, <code>match</code> is somehow expanding the <code>nat</code> match expander as a part of its expansion. Is it using <code>local-expand</code>?</p><p>Well, no. While <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">a previous blog post of mine</a> has illustrated that it is possible to do such a thing with <code>local-expand</code> via some clever trickery, <code>local-expand</code> is really designed to expand <em>expressions</em>. This is a problem, since <code>(nat n)</code> is not an expression, it’s a pattern: it will expand into <code>(? exact-nonnegative-integer? n)</code>, which will lead to a syntax error, since <code>?</code> is not bound in the world of expressions. Instead, for a long while, <code>match</code> and forms like it have emulated how the expander performs macroexpansion in ad-hoc ways. Fortunately, as of Racket v7.0, the new <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Fapply-transformer..rkt%29._local-apply-transformer%29%29"><code>local-apply-transformer</code></a> API provides a way to invoke recursive macroexpansion in a consistent way, and it doesn’t assume that what’s being expanded is an expression.</p><h3><a name="a-closer-look-at-local-apply-transformer"></a>A closer look at <code>local-apply-transformer</code></h3><p>If <code>local-apply-transformer</code> is the answer, what does it actually do? Well, <code>local-apply-transformer</code> allows explicitly invoking a transformer function on some piece of syntax and retrieving the result. In other words, <code>local-apply-transformer</code> allows expanding an arbitrary macro, but since it doesn’t make any assumptions about what the output will be, it only expands it <em>once</em>: just a single step of macro transformation.</p><p>To illustrate, we can write a macro that uses <code>local-apply-transformer</code> to invoke a transformer function and preserve the result using <code>quote-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/apply-transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define-for-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>When we use <code>mac</code>, our <code>flip</code> function will be applied, as a macro, to the syntax object we provide:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="nb">&gt;</span></code></pre><p>Alright, so this works, but it raises some questions. Why is <code>flip</code> defined as a function at phase 1 (using <code>define-for-syntax</code>) instead of as a macro (using <code>define-syntax</code>)? What’s the deal with the <code>'expression</code> argument to <code>local-apply-transformer</code> given that <code>local-apply-transformer</code> is supposedly decoupled from expression expansion? And finally, how is this any different from just calling our <code>flip</code> function on the syntax object directly by writing <code>(flip #'(([x 1]) let x))</code>?</p><p>Let’s start with the first of those questions: why is <code>flip</code> defined as a function rather than as a macro? Well, <code>local-apply-transformer</code> is a fairly low-level operation: remember, it doesn’t assume <em>anything</em> about the argument it’s given! Therefore, it doesn’t take an expression containing a macro and expand it based on its structure, it needs to be explicitly provided the macro transformer function to apply. In practice, this might not seem very useful, since presumably we want to write our macros as macros, not as phase 1 functions. Fortunately, it’s possible to look up the function associated with a macro binding using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, so if we use that, we can define <code>flip</code> using <code>define-syntax</code> as usual:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>Now for the next question: what is the meaning of the <code>'expression</code> argument? This one is more of a historical artifact than anything else: when the expander applies a macro transformer, it does it in a “context”, which is accessible using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-context%29%29"><code>syntax-local-context</code></a> function. This context can be one of a predefined enumeration of cases, including <code>'expression</code>, <code>'top-level</code>, <code>'module</code>, <code>'module-begin</code>, or a list representing a definition context. Whether or not any of those actually apply to our use case, we still have to pick one, but aside from how they affect the value returned by <code>syntax-local-context</code> (which some macros inspect), the value we choose is largely irrelevant. Using <code>'expression</code> will do, even if it’s a bit of a lie.</p><p>Finally, how does any of this differ from just applying the function we get directly? Well, the critical answer is all about <em>hygiene</em>. Racket’s macro system is hygienic, which, among other things, ensures bindings defined with the same name in different places do not unintentionally conflict. Racket’s hygiene mechanism is implemented in the macroexpander, when macro transformers are applied. If we just applied the <code>flip</code> transformer procedure to a syntax object directly, we would circumvent this hygiene mechanism, potentially causing all sorts of problems. By using <code>local-apply-transformer</code>, we ensure hygiene is preserved.</p><p>There is one small problem left with our program, however. Can you spot it? The key is to consider what would happen if we used <code>flip</code> as an ordinary macro, without using <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="n">let:</span><span class="w"> </span><span class="n">bad</span><span class="w"> </span><span class="k">syntax</span> +<span class="w"> </span><span class="n">in:</span><span class="w"> </span><span class="k">let</span></code></pre><p>What happened? Well, remember that when a macro in Racket is used, it receives the whole use site as a syntax object: in this case, <code>#'(flip (([x 1]) let x))</code>. This means that <code>flip</code> ought to be written to parse its argument slightly differently:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span></code></pre><p>Indeed, now that we’ve properly restructured the macro, we can easily switch to using the convenient <code>define-simple-macro</code> shorthand:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This means we also need to update our definition of <code>mac</code> to provide the full syntax object the expander would:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>This might seem redundant, but remember, <code>local-apply-transformer</code> is very low-level! While the convention that <code>(<em>mac</em> . _)</code> is the syntax for a macro transformation might seem obvious, <code>local-apply-transformer</code> makes no assumptions. It just does what we tell it to do.</p><h3><a name="applying-local-apply-transformer"></a>Applying <code>local-apply-transformer</code></h3><p>So what does <code>local-apply-transformer</code> have to do with the problem at the beginning of this blog post? Well, as it happens, we can use <code>local-apply-transformer</code> to implement a macro that allows expansion <em>anywhere</em> using some simple tricks. While it’s true that we cannot magically divine which locations ought to be expanded, what we <em>can</em> do is explicitly annotate which places to expand.</p><p>To do this, we will implement a macro, <code>expand-inside</code>, that looks for subforms annotated with a special <code>$expand</code> identifier and performs macro transformation on those locations before proceeding with ordinary macroexpansion. Using the <code>clj-binding-pairs</code> example from the beginning of this blog post, our solution to that problem will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Put another way, <code>expand-inside</code> will force eager expansion on any subform surrounded with an <code>$expand</code> annotation.</p><p>We’ll start by defining the <code>$expand</code> binding itself. This binding won’t mean anything at all outside of <code>expand-inside</code>, but we’d like it to be a unique binding so that users can rename it (using, <code>rename-in</code>, for example) if they wish. To do this, we’ll use the usual trick of defining it as a macro that always produces an error if it’s ever used:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"illegal outside an ‘expand-inside’ form"</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span></code></pre><p>Next, we’ll implement a syntax class that will form the bulk of our implementation of <code>expand-inside</code>. Since we need to find uses of <code>$expand</code> that might be deeply-nested inside the syntax object provided to <code>expand-inside</code>, we need to recursively look through the syntax object, find any instances of <code>$expand</code>, and put it all back together once we’re done. This can be done relatively cleanly using a recursive syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">do-expand-inside</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="n">$expand</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">$expand</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:do-expand-inside</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">a:do-expand-inside</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">b:do-expand-inside</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">reassembled</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">a.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">reassembled</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">reassembled</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>There are some tricky details to get right in the reassembly of pairs, since syntax lists are actually composed of ordinary pairs rather than syntax pairs, but ultimately, the code for walking a syntax object is small. The key case of this syntax class is the call to <code>do-$expand</code> in the first clause, which we have not yet defined. This function will actually handle performing the expansion by invoking <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">trans</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}})</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">static</span><span class="w"> </span><span class="p">(</span><span class="nb">disjoin</span><span class="w"> </span><span class="nb">procedure?</span><span class="w"> </span><span class="nb">set!-transformer?</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"syntax transformer"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">trans.value</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)])))</span></code></pre><p>This uses the handy <code>static</code> syntax class that comes with <code>syntax/parse</code>, which implicitly handles the call to <code>syntax-local-value</code> and produces a nice error message if the value returned does not match a predicate. All we have to do is apply the transformer value bound to the <code>trans.value</code> attribute using <code>local-apply-transformer</code>, and now the <code>expand-macro</code> can be written in just a couple lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">expand-inside</span> +<span class="w"> </span><span class="kd">#:track-literals</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:do-expand-inside</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form.expansion</span><span class="p">])</span></code></pre><p>(Using the <code>#:track-literals</code> option, also new in Racket v7.0, ensures that Check Syntax will be able to recognize the uses of <code>$expand</code> that disappear from after <code>expand-inside</code> is expanded.)</p><p>Putting everything together, our example from above really works:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>That’s it. All told, the entire implementation is only about 30 lines of code. For a full, compilable, working example, see <a href="https://gist.github.com/lexi-lambda/65d69043023b519694f50dfca2dc7d33">this gist</a>.</p><ol class="footnotes"></ol></article>Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions2018-09-13T00:00:00Z2018-09-13T00:00:00ZAlexis King<article><p>In my <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">previous blog post</a>, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.</p><h2><a name="what-are-internal-definitions"></a>What are internal definitions?</h2><p>This blog post is going to be largely focused on how to properly implement a form that handles the expansion of <em>internal definitions</em> in Racket. This is a tricky topic to get right, but before we can discuss internal definitions, we have to establish what definitions themselves are and how they relate to other binding forms.</p><p>In a traditional Lisp, there are two kinds of bindings: top-level bindings and local bindings. In Scheme and its descendants, this distinction is characterized by two different binding forms, <code>define</code> and <code>let</code>. To a first approximation, <code>define</code> is used for defining top-level, global bindings, and it resembles variable definitions in many mainstream languages in the sense that definitions using <code>define</code> are not really expressions. They don’t produce a value, they define a new binding. Definitions written with <code>define</code> look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="s2">"hello"</span><span class="p">)</span></code></pre><p>Each definition is made up of two parts: the <em>binding identifier</em>, in this case <code>x</code> and <code>y</code>, and the <em>right hand side</em>, or RHS for short. Each RHS is a single expression that will be evaluated and used as the value for the introduced binding.</p><p>In Scheme and Racket, <code>define</code> also supports a shorthand form for defining functions in a natural syntax without the explicit need to write <code>lambda</code>, which looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">double</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span></code></pre><p>However, this is just syntactic sugar. The above form is really just a macro for the following equivalent, expanded version:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">double</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)))</span></code></pre><p>Since we only care about fully-expanded programs, we’ll focus exclusively on the expanded version of <code>define</code> in this blog post, since if we handle that, we’ll also handle the function shorthand’s expansion.</p><p>In contrast to <code>define</code>, there is also <code>let</code>, which has a rather different shape. A <code>let</code> form <em>is</em> an expression, and it creates local bindings in a delimited scope:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>The binding clauses of a <code>let</code> expression are known as the <em>binding pairs</em>, and the sequence of expressions afterwards are known as the <em>body</em> of the <code>let</code>. Each binding pair consists of a binding identifier and a RHS, just like a top-level definition created with <code>define</code>, but while <code>define</code> is a standalone form, the binding pairs cannot meaningfully exist outside of a <code>let</code>—they are recognized as part of the grammar of the <code>let</code> form itself.</p><p>Like other Lisps, Racket distinguishes between top-level—or, more precisely, <em>module-level</em>—bindings and local bindings. A module-level binding can be exported using <code>provide</code>, which will allow other modules to access the binding by importing the module with <code>require</code>. Such definitions are treated specially by the macroexpander, compiler, and runtime system alike. There is a pervasive, meaningful difference between module-level definitions and local definitions besides simply scope.</p><p>I am making an effort to make this as clear as possible before discussing internal definitions because without it, the following point can be rather confusing: internal definitions are written using <code>define</code>, but they are local bindings, <em>not</em> module-level ones! In Racket, <code>define</code> is allowed to appear in the body of virtually all block forms like <code>let</code>, so the following is a legal program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>This program is equivalent to the one expressed using <code>let</code>. In fact, when the Racket macroexpander expands these local uses of <code>define</code>, it actually translates them into uses of <code>letrec</code>. After expanding the above expression, it would look closer to the following:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span></code></pre><p>In this sense, <code>define</code> is a form with a double life in Racket. When used at the module level, it creates module-level definitions, which remain in a fully-expanded program and can be imported by other modules. When used inside local blocks, it creates internal definitions, which do not remain in fully expanded programs, since they are translated into recursive local binding forms.</p><p>In this blog post, we will ignore module-level definitions. Like in the previous blog post, we will focus exclusively on expanding expressions, not whole modules. However, we will extend our language to allow internal definitions inside local binding forms, and we will translate them into <code>letrec</code> forms in the same way as the Racket macroexpander.</p><h2><a name="revisiting-and-generalizing-the-expression-expander"></a>Revisiting and generalizing the expression expander</h2><p>In the previous blog post, our expander expanded types, which were essentially expressions from the perspective of the Racket macroexpander. We wrote a syntax class that handled the parsing of a restricted type grammar that disallowed most Racket-level expression forms, like <code>begin</code>, <code>if</code>, <code>#%plain-lambda</code>, and <code>quote</code>. After all, Hackett is not dependently-typed, and it disallows explicit type abstraction to preserve type inference, so it would be a very bad thing if we allowed <code>if</code> or explicit lambda abstraction to appear in our types. For this blog post, however, we will restructure the type expander to handle the full grammar of expressions permitted by Racket.</p><p>While the syntax class approach used in the previous blog post was cute, this blog post will use ordinary functions defined at phase 1 instead of syntax classes. In practice, this provides superior error reporting, since it reports syntax errors in terms of the form that went wrong, not the form prior to expansion. Since we can still use <code>syntax-parse</code> to parse the arguments to these functions, we don’t lose any expressive power in the expression of our pattern language.</p><p>To start, we’ll extract the call to <code>local-expand</code> into its own function. This corresponds to the <code>type</code> syntax class from the previous blog post, but we’ll use phase 1 parameters to avoid threading so many explicit function arguments around:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-context</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-stop-list</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">))))</span></code></pre><p>Due to the way <code>local-expand</code> implicitly extends the stop list, as discussed in the previous blog post, we can initialize the stop list to a list containing just <code>define-values</code> and <code>define-syntaxes</code>, and the other forms we care about will be included automatically.</p><p>Next, we’ll use this function to implement a <code>expand-expression</code> function, which will emulate the way the expander expands a single expression, as the name implies. We’ll ignore any custom core forms for now, so we’ll just focus exclusively on the Racket core forms.</p><p>A few of Racket’s core forms are not actually subject to any expansion at all, and they expand to themselves. These forms are <code>quote</code>, <code>quote-syntax</code>, and <code>#%variable-reference</code>. Additionally, <code>#%top</code> is not something useful to handle ourselves, since it involves no recursive expansion, so we’ll treat it as if it expands to itself as well and allow the expander to raise any unbound identifier errors it produces. Here’s what the <code>expand-expression</code> function looks like when exclusively handling these things:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Another set of Racket core forms are simple expressions which contain subforms, all of which are themselves expressions. These forms include things like <code>#%expression</code>, <code>begin</code>, and <code>if</code>, and they can be expanded recursively. We’ll add another clause to handle these, which can be written with a straightforward recursive call to <code>expand-expression</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Another easy form to handle is <code>set!</code>, since it also requires simple recursive expansion, but it can’t be handled in the same way as the above forms since one of its subforms (the variable to mutate) should not be expanded. It needs another small clause:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:set!</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)))]</span></code></pre><p>The other expressions are harder, since they’re all the binding forms. Fully-expanded Racket code has four local binding forms: <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, and <code>letrec-values</code>. Additionally, as discussed in the previous blog post, <code>local-expand</code> can also produce <code>letrec-syntaxes+values</code> forms produced by local syntax bindings. In the type expander, we completely disallowed runtime bindings from appearing in the resulting program, so we completely removed <code>letrec-syntaxes+values</code> in our expansion, but in the case of handling arbitrary Racket programs, we actually want to leave a <code>letrec-values</code> form behind to hold any runtime bindings (i.e. the <code>values</code> part of <code>letrec-syntaxes+values</code>).</p><p>We’ll start with <code>#%plain-lambda</code>, which is the simplest of all the five aforementioned binding forms. It binds a sequence of identifiers at runtime, and they are in scope within the body of the lambda expression. Just as we created and used an internal-definition context to hold the bindings of a <code>letrec-syntax+values</code> form in the previous blog post, we’ll do the same for Racket’s other binding forms as well:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>However, the above handling of <code>#%plain-lambda</code> isn’t <em>quite</em> right, since the argument list can also include a “rest argument” binding in addition to a sequence of positional arguments. To accommodate this, we can introduce a simple syntax class that handles the different permutations:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">plain-formals</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"formals"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[[</span><span class="n">id</span><span class="w"> </span><span class="mi">1</span><span class="p">]]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id*:id</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id**:id</span><span class="p">)</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">id*</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">id**</span><span class="p">]]))</span></code></pre><p>Now we can use this to adjust <code>#%plain-lambda</code> to handle rest arguments:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Next, we’ll handle <code>case-lambda</code>. As it turns out, expanding <code>case-lambda</code> is almost exactly the same as expanding <code>#%plain-lambda</code>, except that it has multiple clauses. Since each clause is expanded identically to the body of a <code>#%plain-lambda</code>, and it even has the same shape, the clauses can be extracted into a separate syntax class to share code between the two forms:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]]))</span></code></pre><p>Now, both <code>#%plain-lambda</code> and <code>case-lambda</code> can be handled in a few lines of code each:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Finally, we need to tackle the three <code>let</code> forms. None of these involve any fundamentally new ideas, but they are a little bit more involved than the variants of lambda due to the need to handle the RHSs. Each variant is slightly different, but not dramatically so: the bindings aren’t in scope when expanding the RHSs of <code>let-values</code>, but they are for <code>letrec-values</code> and <code>letrec-syntaxes+values</code>, and <code>letrec-syntaxes+values</code> creates transformer bindings and must evaluate some RHSs in phase 1 while <code>let-values</code> and <code>letrec-values</code> exclusively bind runtime bindings. It would be possible to implement these three forms in separate clauses, but since we’d ideally like to duplicate as little code as possible, we can write a rather elaborate <code>syntax/parse</code> pattern to handle all three binding forms all at once.</p><p>We’ll start by handling <code>let-values</code> alone to keep things simple:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>This isn’t dramatically different from the implementation of <code>#%plain-lambda</code>. The only difference is that we have to recursively invoke <code>expand-expression</code> on the RHSs in addition to expanding the body expressions. To handle <code>letrec-values</code> in the same clause, however, we’ll have to get a little more creative.</p><p>So far, we haven’t actually tapped very far into <code>syntax/parse</code>’s pattern language over the course of these two blog posts. The full language available to patterns is rather extensive, and we can take advantage of that to write a modification of the above clause that handles both <code>let-values</code> and <code>letrec-values</code> at once:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}}}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>The <code>~bind</code> pattern allows us to explicitly control how attributes are bound as part of the pattern-matching process, which allows us to track when we want to enable the recursive binding behavior of <code>letrec-values</code> in our handler code. Since the vast majority of the logic is otherwise identical, this is a significant improvement over duplicating the clause.</p><p>Adding support for <code>letrec-syntaxes+values</code> is done in the same general way, but the pattern is even more involved. In addition to tracking whether or not the bindings are recursive, we have to track if any syntax bindings were present at all, and if they were, bind them with <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span></code></pre><p>This behemoth clause handles all three varieties of <code>let</code> forms that can appear in the result of <code>local-expand</code>. Notably, in the <code>letrec-syntaxes+values</code> case, we expand into <code>letrec-values</code>, since the transformer bindings are effectively erased, and we use <code>syntax-track-origin</code> to record that the result originally came from a use of <code>letrec-syntaxes+values</code>.</p><p>With these five clauses, we’ve handled all the special forms that can appear in expression position in Racket’s kernel language. To tie things off, we just need to handle the cases of a variable reference, which is represented by a bare identifier not bound to syntax, or literal data, like numbers or strings. We can add one more clause at the end to handle those:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span></code></pre><p>Putting them all together, our <code>expand-expression</code> function looks as follows:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> + +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">])))</span></code></pre><p>If we try it out, we’ll see that it really does work! Even complicated local binding forms are handled properly by our expander:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">))))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">(((</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>We are now able to expand arbitrary Racket expressions in the same way that the expander does. While this might not seem immediately useful—after all, we haven’t actually gained anything here over just calling <code>local-expand</code> with an empty stop list—we can use this as the basis of an expander that can extensibly handle custom core forms, which I may cover in a future blog post.</p><h2><a name="adding-support-for-internal-definitions"></a>Adding support for internal definitions</h2><p>In the previous section, we defined an expander that could expand arbitrary Racket expressions, but our expander is still imperfect: we still do not support internal definitions. For all forms that have bodies, including <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, and <code>letrec-syntaxes+values</code>, Racket permits the use of internal definitions.</p><p>In practice, internal-definition contexts allow for an increased degree of modularity compared to traditional local binding forms, since they provide an <em>extensible</em> binding language. Users may mix many different binding forms within a single definition context, such as <code>define</code>, <code>define-syntax</code>, <code>match-define</code>, and even <code>struct</code>. However, this means the rewriting process described earlier in this blog post is not as simple as detecting the definitions and lifting them into a local binding form, since it’s not immediately apparent which forms are binding forms and which are expressions!</p><p>For this reason, expanding internal-definition contexts happens to be a nontrivial problem in itself. It involves a little more care than expanding expressions does, since it requires using partial expansion to discover which forms are definitions and which forms are expressions. We must take care to never expand too much, but also to expand enough that we reveal all uses of <code>define-values</code> and <code>define-syntaxes</code> (which all definition forms eventually expand into). We also must handle the splicing behavior of <code>begin</code>, which is necessary to allow single forms to expand into multiple definitions.</p><p>We’ll start by writing an <code>expand-body</code> function, which operates similarly to our previous <code>expand-expression</code> function. Unlike <code>expand-expression</code>, <code>expand-body</code> will accept a <em>list</em> of syntax objects, which represents the sequence of forms that make up the body. Logically, each body will create a first-class definition context with <code>syntax-local-make-definition-context</code> to represent the sequence of definitions:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>The bulk of our <code>expand-body</code> function will be a loop that partially expands body forms, adds definitions to the definition context as it discovers them, and returns the expressions and runtime definitions to be rewritten into binding pairs for a <code>letrec-values</code> form. Additionally, the loop will also track so-called <em>disappeared uses</em> and <em>disappeared bindings</em>, which are attached to the expansion using syntax properties to allow tools like DrRacket to learn about the binding structure of phase 1 definitions that are erased as part of macroexpansion.</p><p>The skeleton of this loop is relatively straightforward to write. We will iterate over the syntax objects that make up the body, expand them, and process the expansion using <code>syntax-parse</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">)))))))</span></code></pre><p>The hard part, of course, is actually handling the potential results of that expansion. We need to handle three forms specially: <code>begin</code>, <code>define-values</code>, and <code>define-syntaxes</code>. All other results of partial expansion will be treated as expressions. We’ll start by handling <code>begin</code>, since it’s the simplest case; we only need to prepend the subforms to the list of body forms to be processed, then continue looping:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>However, as is often the case, this isn’t quite perfect, since the information that these forms came from a surrounding <code>begin</code> is lost, which tools like DrRacket want to know. To solve this problem, the expander adjusts the <code>origin</code> property for all spliced forms, which we can mimic using <code>syntax-track-origin</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This is sufficient for <code>begin</code>, so we can move onto the actual definitions themselves. This actually isn’t too hard, since we just need to add the bindings we discover to the first-class definition context and preserve <code>define-values</code> bindings as binding pairs:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This solution is missing one thing, however, which is the use of <code>syntax-local-identifier-as-binding</code> to any use-site scopes that were added to the binding identifier while expanding the binding form in the definition context. Explaining precisely why this is necessary is outside the scope of this blog post, and is best understood by reading <a href="http://www.cs.utah.edu/plt/scope-sets/pattern-macros.html#%28part._use-site%29">the section on use-site scopes</a> in the paper that describes the theory behind Racket’s current macro system, Bindings as Sets of Scopes. In any case, the impact on our implementation is small:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>Finally, as with <code>begin</code>, we want to track that the binding pairs we generate actually came from a use of <code>define-values</code> (which in turn likely came from a use of some other definition form). Therefore, we’ll add another use of <code>syntax-track-origin</code> to copy and extend the necessary properties:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>That’s it for <code>define-values</code>. All that’s left is to handle <code>define-syntaxes</code>, which is quite similar, but instead of storing the definition in a binding pair, its RHS is immediately evaluated and added to the definition context using <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span></code></pre><p>As the above snippet indicates, this is also where the disappeared uses and disappeared bindings come in. In previous cases, we’ve used <code>syntax-track-origin</code> to indicate that a piece of syntax was the result of expanding a different piece of syntax, but in this case, <code>define-syntaxes</code> doesn’t expand into anything at all; it’s simply removed from the expansion entirely. Therefore, we need to resort to tracking the information in syntax properties on the resulting <code>letrec-values</code> form, so we’ll save them for later.</p><p>Finally, to finish things up, we can add a catchall clause that handles all other forms, which are now guaranteed to be expressions:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This completes our loop that processes definition forms, so all that’s left to do is handle the results. The only significant remaining work is to actually expand the RHSs of the binding pairs we collected and the body expressions, which can be done by calling our own <code>expand-expression</code> function directly:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span></code></pre><p>Finally, we can assemble all the pieces together into a single local binding form with the appropriate syntax properties:</p><pre><code class="pygments"><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))</span></code></pre><p>That’s it. We’ve now written an <code>expand-body</code> function that can process internal definition contexts in the same way that the macroexpander does. Overall, the whole function is just under 45 lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)))))</span></code></pre><p>The next step is to actually use this function. We need to replace certain recursive calls to <code>expand-expression</code> with calls to <code>expand-body</code>, but if we do this naïvely, we’ll have some problems. Currently, when we expand body forms, they’re always immediately inside another definition context (i.e. the bindings introduced by lambda formals or by <code>let</code> binding pairs), but they haven’t actually been expanded in that context yet. When we call <code>expand-body</code>, we create a nested context, which will inherit the bindings, but won’t automatically add the parent context’s scope. Therefore, we need to manually call <code>internal-definition-context-introduce</code> on the body syntax objects before calling <code>expand-body</code>. We can write a small helper function to make this easier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="n">stxs</span><span class="w"> </span><span class="n">ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">ctx</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stxs</span><span class="p">))))))</span></code></pre><p>Now we just need to replace the relevant calls to <code>expand-expression</code> with calls to <code>expand-body/in-ctx</code>, starting with a minor adjustment to our <code>lambda-clause</code> syntax class from earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="p">]]))</span></code></pre><p>The only other change must occur in the handling of the various <code>let</code> forms, which similarly replaces <code>expand-expression</code> with <code>expand-body/in-ctx</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">)))]</span></code></pre><p>With these changes, we’ve now extended our expression expander with the ability to expand internal definitions. We can see this in action on a simple example:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>Just as we’d like, the transformer bindings were expanded and subsequently eliminated, and the runtime binding was collected into a <code>letrec-values</code> form. The outer <code>let-values</code> is left over from the outer <code>let</code>, which is needed only to create an internal-definition context to hold our internal definitions.</p><h2><a name="putting-the-expression-expander-to-work"></a>Putting the expression expander to work</h2><p>So far, we’ve done a lot of work to emulate the behavior of Racket’s macroexpander, and as the above example demonstrates, we’ve been fairly successful in that goal. However, you might be wondering <em>why</em> we did any of this, as replicating the behavior of <code>local-expand</code> is not very useful on its own. As mentioned above, this can be used as the foundation of an expander for custom core forms that extends, rather than replaces, the built-in Racket core forms, It can also be used to “cheat” and expand through the behavior of the <code>local-expand</code> stop list, which implicitly adds the Racket core forms to any non-empty stop list. Hopefully, I’ll have a chance to cover some of these things more deeply in the future, but for now, I’ll just give a small taste of the latter.</p><p>By using the power of our <code>expand-expression</code> function, it’s actually possible to use this kind of expression expander to do genuinely nefarious things, such as hijack the behavior of arbitrary macros! For example, we could do something evil like make <code>for</code> loops run in reverse order by adding <code>for</code> to <code>current-stop-list</code>, then adding an additional special case to <code>expand-expression</code> for <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">for</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="k">for</span><span class="p">]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:for</span><span class="w"> </span><span class="p">([</span><span class="n">x:id</span><span class="w"> </span><span class="n">seq:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="p">(</span><span class="nb">sequence-&gt;list</span><span class="w"> </span><span class="n">seq</span><span class="p">)))]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>Amazingly, due to the fact that we’ve taken complete control of the expansion process, this will rewrite uses of <code>for</code> <em>even if they are introduced by macroexpansion</em>. For example, we could write a small macro that expands into a use of <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="nb">in-range</span><span class="w"> </span><span class="n">n</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">i</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span> +<span class="mi">0</span> +<span class="mi">1</span> +<span class="mi">2</span> +<span class="mi">3</span> +<span class="mi">4</span></code></pre><p>If we write a wrapper macro that applies our evil version of <code>expand-expression</code> to its body, then wrap a use of our <code>print-up-to</code> macro with it, it will execute the loop in reverse order:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:expr</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span><span class="p">)])</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>On its own, this is not that impressive, since we could have just used <code>local-expand</code> on the body directly to achieve this. However, what’s remarkable about <code>hijack-for-loops</code> is that it will work even if the <code>for</code> loop is buried deep inside some arbitrary expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">foo</span> +<span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">))))</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="mi">5</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>Of course, this example is rather contrived—mucking with <code>for</code> loops like this isn’t useful at all, and nobody would really write <code>print-up-to</code> as a macro, anyway—but there is potential for using this technique to do more interesting things.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p>The system outlined in this blog post is not something I would recommend using in any real macro. It is enormously complicated, requires knowledge well above that of your average working macrologist, and it involves doing rather horrible things to the macro system, things it was undoubtably never designed to do. Still, I believe this blog post is useful, for a few different reasons:</p><ol><li><p>The technology outlined in this post, while perhaps not directly applicable to existing real-world problems, provides a framework for implementing various new kinds of syntax transformations in Racket <em>without</em> extending the macro system. It demonstrates the expressive power of the macro system, and it hopefully lays the foundation for a better, more high-level interface for users who wish to define their own languages with custom core forms.</p></li><li><p>This system provides insight into the way the Racket macroexpander operates, <em>in terms of the userspace syntax API</em>. The canonical existing model of hygienic macroexpansion, in the aforementioned <a href="http://www.cs.utah.edu/plt/scope-sets/">Bindings as Sets of Scopes</a> paper, does not explain the workings of internal definition contexts in detail, and it certainly doesn’t explain them in terms that a Racket programmer would already be familiar with. By reencoding those ideas within the macro system itself, an advanced macro writer may be able to more easily connect concepts in the macro system’s implementation to concepts they have already been exposed to.</p></li><li><p>The capability of the proof-of-concept outlined here demonstrates that the limitation imposed by the existing implementation of the stop list (namely, the way it is implicitly extended with additional identifiers) is essentially artificial, and it can be hacked around with sufficient (albeit significant) effort. This isn’t enormously important, but it is somewhat relevant to a recent debate in <a href="https://github.com/racket/racket/issues/2154">a GitHub issue</a> about the handling of the <code>local-expand</code> stop list.</p></li><li><p>Finally, for myself as much as anyone else, this implementation records in a concise way (perhaps overly concise at times) the collection of very subtle details I’ve learned over the past six months about how information is preserved and propagated during the expansion process.</p></li></ol><p>This blog post is not for everybody. If you made it to the end, give yourself a pat on the back. If you made it to the end <em>and</em> understood everything you read: congratulations, you are a certified expert in Racket macro programming. If not, do not fear, and do not lose hope—I plan for something significantly more mellow next time.</p><p>As always, I’d like to give thanks to the people who contributed significantly, if indirectly, to the contents of this blog post, namely <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://mballantyne.net">Michael Ballantyne</a>, and <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a>. And finally, for those interested, all of the code in this blog post can be found in a runnable form <a href="https://gist.github.com/lexi-lambda/c4f4b91ac9c0a555447d72d02e18be7b">in this GitHub gist</a>.</p><ol class="footnotes"></ol></article>Reimplementing Hackett’s type language: expanding to custom core forms in Racket2018-04-15T00:00:00Z2018-04-15T00:00:00ZAlexis King<article><p>In the past couple of weeks, I <a href="https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935">completely rewrote the implementation of Hackett’s type language</a> to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.</p><p>This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">my previous blog post on Hackett</a>, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.</p><h2><a name="what-are-core-forms"></a>What are core forms?</h2><p>Before we can get started writing <em>custom core forms</em>, we need to understand the meaning of Racket’s plain old <em>core forms</em>. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.</p><p>To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a <code>#lang</code> line at the top. In this case, we’ll use <code>#lang racket</code> to keep things simple:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as <em>reading</em> the program. The <code>#lang</code> line controls how the program is read—some <code>#lang</code>s provide parsers that allow syntax that is very different from the parser used for <code>#lang racket</code>—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span></code></pre><p>Note the introduction of <code>#%module-begin</code>. Despite the fancy name, this is really just an ordinary macro provided by the <code>racket</code> language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with <code>#%module-begin</code>; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.</p><p>One the program has been read, it is subsequently <em>expanded</em> by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">2</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">call-with-values</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="n">add2</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">))</span> +<span class="w"> </span><span class="n">print-values</span><span class="p">)))</span></code></pre><p>Let’s note the things that changed:</p><ol><li><p><code>#%module-begin</code> was replaced with <code>#%plain-module-begin</code>. <code>#%plain-module-begin</code> is a binding that wraps the body of every expanded module, and all definitions of <code>#%module-begin</code> in any language must eventually expand to <code>#%plain-module-begin</code>. However, <code>#lang racket</code>’s <code>#%module-begin</code> doesn’t <em>just</em> expand to <code>#%plain-module-begin</code>, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints <code>5</code> even though there is no code related to printing in the original program!</p></li><li><p>The lambda shorthand used with <code>define</code> was converted to an explicit use of <code>lambda</code>, and it was expanded to <code>define-values</code>. In Racket, <code>define</code> and <code>define-syntax</code> are really just macros for <code>define-values</code> and <code>define-syntaxes</code> that only bind a single identifier.</p></li><li><p>All function applications were tagged explicitly with <code>#%plain-app</code>. This syntactically distinguishes function applications from uses of forms like <code>define-values</code> or <code>lambda</code>. It also allows languages to customize function application by providing their own macros named <code>#%app</code> (just like languages can provide their own macros named <code>#%module-begin</code> that expand to <code>#%plain-module-begin</code>), but that is outside the scope of this blog post.</p></li><li><p>All literals have been wrapped with <code>quote</code>, so <code>2</code> became <code>'2</code> and <code>3</code> became <code>'3</code>.</p></li></ol><p>Importantly, the resulting program contains <strong>no macros</strong>. Such programs are called <em>fully expanded</em>, since all macros have been eliminated and no further expansion can take place.</p><p>So what’s left behind? Well, some of the things in the program are literal data, like the numbers <code>2</code> and <code>3</code>. There are also some variable references, <code>x</code> and <code>add2</code>. Most of the program, however, is built out of primitives like <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, and <code>lambda</code>. These primitives are <em>core forms</em>—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.</p><p>In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:</p><pre><code class="pygments"><span class="n">var</span> <span class="n">add2</span> <span class="o">=</span> + <span class="n">function</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span> + +<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">add2</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span></code></pre><p>Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be <em>compiled</em>. The Racket compiler has built-in rules for how to compile core forms like <code>define-values</code>, <code>lambda</code>, and <code>#%plain-app</code>, and the result is optimized Racket bytecode.</p><p>In the remainder of this blog post, as most discussions of macros do, we’ll ignore the <em>read</em> and <em>compile</em> steps of the Racket program pipeline and focus exclusively on the <em>expand</em> step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.</p><h3><a name="racket-s-default-core-forms"></a>Racket’s default core forms</h3><p>So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, <code>lambda</code>, and <code>quote</code>, but there are many more. The full list is available in the section of the Racket reference named <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29">Fully Expanded Programs</a>, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like <code>define-syntaxes</code>, <code>if</code>, <code>let-values</code>, <code>letrec-values</code>, <code>begin</code>, <code>quote-syntax</code>, and <code>set!</code>. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.</p><p>These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, <code>#lang racket</code> implements <code>cond</code> as a macro that eventually expands into <code>if</code>, and it implements <code>syntax</code> as a macro that eventually expands into function calls and <code>quote-syntax</code>. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s <code>match</code> can expand into uses of <code>let</code> and <code>cond</code>, and it doesn’t need to concern itself with using <code>let-values</code> and <code>if</code>. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.</p><h3><a name="the-need-for-custom-core-forms"></a>The need for custom core forms</h3><p>With that in mind, why might we wish to define <em>custom</em> core forms? In fact, what would such a thing even mean? By their very nature, <em>all</em> Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.</p><p>Despite this, there <em>are</em> at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.</p><h4><a name="supporting-multiple-backends"></a>Supporting multiple backends</h4><p>The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket <code>#lang</code> that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might <em>also</em> wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.</p><p>In the case of a hardware description language, the custom core forms might include things like <code>input</code> and <code>output</code> for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could <em>additionally</em> define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.</p><p>Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.</p><h4><a name="compiling-an-extensible-embedded-language"></a>Compiling an extensible embedded language</h4><p>A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.</p><p>This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it <em>will</em> eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.</p><p>Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the <code>match</code> macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.</p><p>Existing DSLs of this type are rare, but they do exist. <code>syntax/parse</code> provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from <code>racket/match</code> to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While <code>match</code> is essentially just a traditional pattern-matcher, albeit an extensible one, <code>syntax-parse</code> is its own programming language, closer in some ways to Prolog than to Racket.</p><p>For this reason, <code>syntax/parse</code> has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as <a href="http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29"><em>pattern directives</em></a>. Here is an example of pattern directives in action, from my own <code>threading</code> library:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>Each directive is represented by a keyword, in this case <code>#:do</code> and <code>#:with</code>. Each directive has a corresponding keyword in the pattern language, in this case <code>~do</code> and <code>~parse</code>. Therefore, the above pattern could equivalently be written this way:</p><pre><code class="pygments"><span class="p">[{</span><span class="n">~and</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">~do</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using <code>#:declare</code>, so this is also equivalent:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">expr</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a <code>#:with</code> or <code>~parse</code> pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using <code>#:do</code> or <code>~do</code>, practical uses of <code>syntax/parse</code> really do involve quite a lot of programming in its pattern DSL.</p><p>But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—<code>#:declare</code>, <code>#:do</code>, and <code>#:with</code>, among others—are essentially the core forms of <code>syntax/parse</code>’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.</p><p>But what if <code>syntax/parse</code> could define its own core forms? What if, instead of <code>#:do</code>, <code>#:declare</code>, and <code>#:with</code> being implemented as keyword options specially recognized by the <code>syntax-parse</code> grammar, it defined <code>do</code>, <code>declare</code>, and <code>with</code> as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the <code>syntax/parse</code> core forms. The implementation of <code>syntax/parse</code> could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.</p><p>Now, to be fair, <code>syntax/parse</code> is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.</p><p>The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.</p><h2><a name="the-need-for-a-custom-type-language-in-hackett"></a>The need for a custom type language in Hackett</h2><p>In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for <em>both</em> of them:</p><ul><li><p>Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.</p></li><li><p>Hackett can <em>also</em> make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.</p></li></ul><p>The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:</p><ul><li><p><code>(#%type:con <em>id</em>)</code> — Type constructors, like <code>Integer</code> or <code>Maybe</code>. These are one of the fundamental building blocks of Hackett types.</p></li><li><p><code>(#%type:app <em>type</em> <em>type</em>)</code> — Type application, such as <code>(Maybe Integer)</code>. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of <code>#%type:app</code>.</p></li><li><p><code>(#%type:forall <em>id</em> <em>type</em>)</code> — Universal quantification. This is essentially a binding form, which binds any uses of <code>(#%type:bound-var <em>id</em>)</code> in <code><em>type</em></code>.</p></li><li><p><code>(#%type:qual <em>type</em> <em>type</em>)</code> — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like <code>Eq</code> are bound as type constructors.</p></li><li><p>Finally, Hackett types support three different varieties of type variables:</p><ul><li><p><code>(#%type:bound-var <em>id</em>)</code> — Bound type variables. These are only legal under a corresponding <code>#%type:forall</code>.</p></li><li><p><code>(#%type:wobbly-var <em>id</em>)</code> — Solver variables, which may unify with any other type as part of the typechecking process.</p></li><li><p><code>(#%type:rigid-var <em>id</em>)</code> — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.</p></li></ul></li></ul><p>To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we <em>can</em> bind them to macros that do nothing but raise an exception if something attempts to expand them:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span></code></pre><p>This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.</p><h3><a name="expanding-types-in-our-type-language"></a>Expanding types in our type language</h3><p>We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we <em>do</em> want to expand some of their subforms. For example, in the type <code>(#%type:app a b)</code>, we want to recursively expand <code>a</code> and <code>b</code>.</p><p>In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a>, and it has an option relevant to our needs: the stop list.</p><p>Often, <code>local-expand</code> is used to force the expander to completely, recursively expand a form. For example, by using <code>local-expand</code>, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="c1">; =&gt; (let-values ([(x) &#39;1]) (#%plain-app + x &#39;2))</span></code></pre><p>The third argument to <code>local-expand</code> is the <em>stop list</em>, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; =&gt; (#%type:forall x t)</span></code></pre><p>Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the <code>#%type:forall</code> identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding <code>t</code> since the expander has no way of knowing which pieces of <code>(#%type:forall x t)</code> it should expand! In this case, we want it to recur to expand <code>t</code>, since it should be a type, but not <code>x</code>, since <code>#%type:forall</code> essentially puts <code>x</code> in binding position.</p><p>Therefore, we have to get more clever. We need to call <code>local-expand</code> to produce a type, then we have to pattern-match on it and subsequently call <code>local-expand</code> <em>again</em> on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.</p><p>One good way to do this is to use <code>syntax/parse</code> syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using <code>local-expand</code> and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled <code>#%type:con</code> and <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:expanded-type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]))</span></code></pre><p>This blog post is definitely <em>not</em> a <code>syntax/parse</code> tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named <code>expansion</code>. This attribute contains the fully expanded version of the type currently being parsed. In the <code>#%type:con</code> case, <code>expansion</code> is just <code>this-syntax</code>, which holds the current piece of syntax being parsed. This makes sense, since uses of <code>#%type:con</code> just expand to themselves—expanding <code>(#%type:con Maybe)</code> should not perform any additional expansion on <code>Maybe</code>. This is one of Hackett’s atomic types.</p><p>In contrast, <code>#%type:app</code> <em>does</em> recursively expand its arguments. By annotating its two subforms with <code>:type</code>, the <code>type</code> syntax class will invoke <code>local-expand</code> on each subform, which will in turn use <code>expanded-type</code> to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once <code>a</code> and <code>b</code> have been expanded, <code>#%type:app</code> reassembles them into a new syntax object using <code>#'(#%type:app a.expansion b.expansion)</code>, which replaces their unexpanded versions with their new, expanded versions.</p><p>We can see this behavior by writing a small <code>expand-type</code> function that will expand its argument:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>Now we can use it to observe what happens when we try expanding a type using <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; #%type:app: expected type</span> +<span class="c1">; at: Maybe</span> +<span class="c1">; in: (#%type:app Maybe Integer)</span></code></pre><p>Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined <code>Maybe</code> or <code>Integer</code> anywhere. Let’s do so! We can define them as simple macros that expand into uses of <code>#%type:con</code>, which can be done easily using <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29"><code>make-variable-like-transformer</code></a> from <code>syntax/transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Maybe</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Integer</span><span class="p">)))</span></code></pre><p>Now, if we try expanding that same type again:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Integer))</span></code></pre><p>…it works! Neat. Now we just need to add the cases for the remaining forms in our type language:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">t:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like <code>let-syntax</code> to work in types. For example, we ought to be able to create a local type binding with <code>let-syntax</code> and have it just work. Unfortunately, it doesn’t:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; let-syntax: expected one of these identifiers: `#%type:con&#39;, `#%type:app&#39;, `#%type:forall&#39;, `#%type:qual&#39;, `#%type:bound-var&#39;, `#%type:wobbly-var&#39;, or `#%type:rigid-var&#39;</span> +<span class="c1">; at: letrec-syntaxes+values</span> +<span class="c1">; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))</span></code></pre><p>What went wrong? And why is it complaining about <code>letrec-syntaxes+values</code>? Well, if you read the documentation for <code>local-expand</code>, you’ll find that its behavior is a little more complicated than you might at first believe:</p><blockquote><p>If <em><code>stop-ids</code></em> is [a nonempty list containing more than just <code>module*</code>], then <code>begin</code>, <code>quote</code>, <code>set!</code>, <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, <code>if</code>, <code>begin0</code>, <code>with-continuation-mark</code>, <code>letrec-syntaxes+values</code>, <code>#%plain-app</code>, <code>#%expression</code>, <code>#%top</code>, and <code>#%variable-reference</code> are implicitly added to <em><code>stop-ids</code></em>. Expansion stops when the expander encounters any of the forms in <em><code>stop-ids</code></em>, and the result is the partially-expanded form.</p></blockquote><p>That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with <code>letrec-syntaxes+values</code> (which <code>let-syntax</code> expands to) is a reasonable one. If the expander naïvely expanded <code>letrec-syntaxes+values</code> in the presence of a nonempty stop list, it could cause some significant problems!</p><p>Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">))</span></code></pre><p>We see <code>let-syntax</code>, so we start by evaluating the expression on the right hand side of the <code>Bool</code> binding. This produces a transformer expression, so we bind <code>Bool</code> to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:</p><pre><code class="pygments"><span class="c1">; local bindings:</span> +<span class="c1">; Bool -&gt; #&lt;variable-like-transformer&gt;</span> +<span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)</span></code></pre><p>Now, the identifier in application position is <code>#%type:app</code>, and <code>#%type:app</code> is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the <code>let-syntax</code> needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is <code>(#%type:app Maybe Bool)</code>. But this is a problem, because when we then go to expand <code>Bool</code>, <code>Bool</code> isn’t in the local binding table anymore! The <code>let-syntax</code> was already erased, and <code>Bool</code> is unbound!</p><p>When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.</p><p>Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call <code>local-expand</code>. The bad news is that first-class definition contexts are <em>complicated</em>, and using them properly is a surprisingly subtle problem.</p><p>Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our <code>type</code> and <code>expanded-type</code> syntax classes so that we may thread a definition context around as we recursively expand:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now, we can add an additional case to <code>expanded-type</code> to handle <code>letrec-syntaxes+values</code>, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>But even this isn’t quite right. The problem with this implementation is that it throws away the existing <code>intdef-ctx</code> argument to <code>expanded-type</code>, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a <em>child</em> of the previous definition context by passing the old context as an argument to <code>syntax-local-make-definition-context</code>. This will ensure the parent bindings are brought into scope when expanding using the child context:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>With this in place, our example using <code>let-syntax</code> actually works!</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Bool))</span></code></pre><p>Pretty cool, isn’t it?</p><h3><a name="preserving-syntax-properties-and-source-locations"></a>Preserving syntax properties and source locations</h3><p>We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.</p><p>To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports <code>#%type:app</code> under a different name, like <code>#%type:apply</code>, we should expand to a piece of syntax that still has <code>#%type:apply</code> in application position instead of replacing it with <code>#%type:app</code>.</p><p>To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the <code>#%type:app</code> clause to the following:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span></code></pre><p>But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy <em>everything</em>, we can define two helper macros, <code>syntax/loc/props</code> and <code>quasisyntax/loc/props</code>, which are like <code>syntax/loc</code> and <code>quasisyntax/loc</code> but copy properties in addition to source location information:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span></code></pre><p>Using <code>syntax/loc/props</code>, we can be truly thorough about ensuring all properties are preserved:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span></code></pre><p>Applying this to the other relevant clauses, we get an updated version of the <code>expanded-type</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now we’re getting closer, but if you can believe it, even <em>this</em> isn’t good enough. The real expander’s implementation of <code>letrec-syntaxes+values</code> does two things our implementation does not: it copies properties and updates the <code>'origin</code> property to indicate the syntax came from a use of <code>letrec-syntaxes+values</code>, and it adds a <code>'disappeared-use</code> property to record the erased bindings for use by tools like DrRacket. We can apply <code>syntax-track-origin</code> and <code>internal-definition-context-track</code> to the resulting syntax to add the same properties the expander would:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span></code></pre><p>Now we’ve <em>finally</em> dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.</p><h3><a name="connecting-our-custom-language-to-hackett"></a>Connecting our custom language to Hackett</h3><p>It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-meta</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/parse</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/intdef</span> +<span class="w"> </span><span class="n">threading</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a <em>typechecker</em>. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.</p><p>But we don’t <em>just</em> need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom <code>#%app</code> binding that converts n-ary type applications to nested uses of <code>#%type:app</code>, as well as a nicer <code>forall</code> macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of <code>#%app</code>—one for types and one for values—since <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">Hackett supports multiple namespaces</a>, and each can have its own <code>#%app</code> binding.</p><p>The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom <code>syntax/parse</code> pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, <a href="https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt">it’s available on GitHub here</a>.</p><h2><a name="evaluation-limitations-and-acknowledgements"></a>Evaluation, limitations, and acknowledgements</h2><p>Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started <a href="https://twitter.com/lexi_lambda/status/976533916596097024">my new job</a> this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net <em>removal</em> of 100 lines from the rest of the codebase, which I consider an organizational win.</p><p>As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I <a href="https://twitter.com/lexi_lambda/status/985051504867446786">able to get it working</a> quickly and easily, I was able to do it in <a href="https://twitter.com/lexi_lambda/status/985052476473856000">no more than 20 lines of code</a>. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.</p><p>Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the <code>local-expand</code> stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes <code>syntax-parameterize</code>, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like <code>syntax-parameterize</code> cooperate better with things like Hackett’s type language.</p><p>Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> for <a href="https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf">his incredible work on constructing tools for the working macro developer</a>, including writing the fantastic <code>syntax/parse</code> library that powers essentially everything I do. Thank you both.</p><ol class="footnotes"></ol></article>A space of their own: adding a type namespace to Hackett2017-10-27T00:00:00Z2017-10-27T00:00:00ZAlexis King<article><p>As previously discussed on this blog, <a href="https://github.com/lexi-lambda/hackett">my programming language, Hackett</a>, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?</p><p>For now, at least, the answer is that Hackett will emulate Haskell: <strong>Hackett now has two namespaces</strong>. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.</p><h2><a name="why-two-namespaces"></a>Why two namespaces?</h2><p>Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.</p><p>This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="ss">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="ss">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="ss">c</span><span class="p">)))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly <em>annotate</em> when they wish to use a value from a different namespace:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">mapcar</span><span class="w"> </span><span class="nf">#&#39;</span><span class="nb">car</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="nv">c</span><span class="p">)))</span> +<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>The Common Lisp <code>#'x</code> reader abbreviation is equivalent to <code>(function x)</code>, and <code>function</code> is a special form that references a value in the function namespace.</p><p>While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.</p><p>However, Hackett is a little different from all of the aforementioned languages because Hackett has <em>types</em>. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is <em>always</em> syntactically unambiguous.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Even if types and values live in separate namespaces, there is no need for a <code>type</code> form a la CL’s <code>function</code> because it can always be determined implicitly.</p><p>For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span></code></pre><p>This defines a binding named <code>Tuple</code> at the type level, which is a <em>type constructor</em> of two arguments that produces a type of kind <code>*</code>,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> and another binding named <code>Tuple</code> at the value level, which is a <em>value constructor</em> of two arguments that produces a value of type <code>(Tuple a b)</code>.</p><p>But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor <code>tuple</code> instead of <code>Tuple</code>? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>This works fine. But what happens if the programmer decides to change the name of the <code>bar</code> value?</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">qux</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>Can you spot the bug? Disturbingly, this code <em>still compiles</em>! Even though <code>bar</code> is not a member of <code>Foo</code> anymore, it’s still a valid pattern, since names used as patterns match anything, just as the <code>y</code> pattern matches against any integer inside the <code>baz</code> constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: <code>(foo-&gt;integer (baz 42))</code> will still produce <code>0</code>, not <code>42</code>, since the first case always matches.</p><p>Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the <code>Tuple</code> example above, which is illegal if a programming language only supports a single namespace.</p><p>Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The <code>Tuple</code> example from above is perfectly legal Hackett.</p><h2><a name="adding-namespaces-to-a-language"></a>Adding namespaces to a language</h2><p>Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to <em>implement</em> such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?</p><p>Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: <strong>the remainder of this blog post is <em>highly technical</em>, and some of it involves some of the more esoteric corners of Racket’s macro system</strong>. This blog post is <em>not</em> representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.</p><h3><a name="namespaces-as-scopes"></a>Namespaces as scopes</h3><p>With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as <a href="https://www.cs.utah.edu/plt/scope-sets/"><em>sets of scopes</em></a>, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.</p><p>Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the <em>value scope</em>, and all identifiers in type position must have a different scope, which we will call the <em>type scope</em>. How do we create these scopes and apply them to identifiers? In Racket, we use a function called <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29"><code>make-syntax-introducer</code></a>, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can <em>add</em> the scope to all pieces of the syntax object, <em>remove</em> the scope, or <em>flip</em> the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call <code>make-syntax-introducer</code> once for each namespace:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>We define these in a <code>begin-for-syntax</code> block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Each of these two forms is like <code>begin</code>, which is a Racket form that is, for our purposes, essentially a no-op, but it applies <code>value-introducer</code> or <code>type-introducer</code> to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">value-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">type-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span></code></pre><p>This program produces the following output:</p><pre><code>'value-x +'type-x +</code></pre><p>It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.</p><p>However, although this is close, it isn’t <em>quite</em> right. What happens if we nest the two inside each other?</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span></code></pre><pre><code>x: identifier's binding is ambiguous + context...: + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) + #(190359 intdef) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) +</code></pre><p>Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of <code>x</code> is <em>ambiguous</em> because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of <code>begin/value</code> or <code>begin/type</code> to <em>override</em> outer ones, ensuring that a use can only be in a single namespace at a time.</p><p>To do this, we simply need to adjust <code>begin/value</code> and <code>begin/type</code> to remove the other scope in addition to adding the appropriate one:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Now our nested program runs, and it produces <code>'type-x</code>, which is exactly what we want—the “nearest” scope wins.</p><p>With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.</p><h3><a name="namespaces-that-cross-module-boundaries"></a>Namespaces that cross module boundaries</h3><p>The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.</p><p>Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the <em>other</em> module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export <em>symbols</em>, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named <code>foo</code>, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the <code>require</code> form’s job to attach the correct scopes to imported identifiers.</p><p>This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup> This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary <em>points</em> of having multiple namespaces.</p><p>What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s <code>require</code> and <code>provide</code> forms are extensible, it’s even possible to implement this mangling in a completely invisible way.</p><p>Currently, the scheme that Hackett uses is to prefix <code>#%hackett-type:</code> onto the beginning of any type exports. This can be defined in terms of a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29"><em>provide pre-transformer</em></a>, which is essentially a macro that cooperates with Racket’s <code>provide</code> form to control the export process. In this case, we can define our <code>type-out</code> provide pre-transformer in terms of <a href="https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29"><code>prefix-out</code></a>, a form built-in to Racket that allows prefixing the names of exports:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">type-out</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-provide-pre-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="w"> </span><span class="n">modes</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">pre-expand-export</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">prefix-out</span><span class="w"> </span><span class="n">#%hackett-type:</span> +<span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">type-introducer</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-out</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span> +<span class="w"> </span><span class="n">modes</span><span class="p">)]))))</span></code></pre><p>Note that we call <code>type-introducer</code> in this macro! That’s because we want to ensure that, when a user writes <code>(provide (type-out Foo))</code>, we look for <code>Foo</code> in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that <code>provide</code> knows <em>which</em> <code>Foo</code> is being provided.</p><p>Once we have referenced the correct binding, the use of <code>prefix-out</code> will appropriately add the <code>#%hackett-type:</code> prefix, so the exporting side is already done. Users do need to explicitly write <code>(type-out ....)</code> if they are exporting a particular type-level binding, but this is rarely necessary, since most users use <code>data</code> or <code>class</code> to export datatypes or typeclasses respectively, which can be modified to use <code>type-out</code> internally. Very little user code actually needs to change to support this adjustment.</p><p>Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the <code>#%hackett-type:</code> prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.</p><p>Just as we extended <code>provide</code> with a provide pre-transformer, we can extend <code>require</code> using a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29"><em>require transformer</em></a>. In code, this entire process looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">and~&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">regexp-match</span><span class="w"> </span><span class="sr">#rx"^#%hackett-type:(.+)$"</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="nb">second</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">unmangle-types-in</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-require-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">imports</span><span class="w"> </span><span class="n">sources</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">expand-import</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-in</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">match-lambda</span> +<span class="w"> </span><span class="p">[(</span><span class="k">and</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="n">local-id</span><span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">local-name</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol-&gt;string</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">local-id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">unmangled-type-name</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">local-name</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">unmangled-type-name</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">unmangled-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;symbol</span><span class="w"> </span><span class="n">unmangled-type-name</span><span class="p">)</span> +<span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="n">local-id</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">unmangled-id</span><span class="p">)</span> +<span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="n">i</span><span class="p">))])</span> +<span class="w"> </span><span class="n">imports</span><span class="p">)</span> +<span class="w"> </span><span class="n">sources</span><span class="p">)])))</span></code></pre><p>This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:</p><ol><li><p>It iterates over each import and calls <code>unmangle-type-name</code> on the imported symbol. If the result is <code>#f</code>, that means the import does not have the <code>#%hackett-type:</code> prefix, and it can be safely passed through unchanged.</p></li><li><p>If <code>unmangle-type-name</code> does <em>not</em> return <code>#f</code>, then it returns the unprefixed name, which is then provided to <code>datum-&gt;syntax</code>, which allows users to forge new identifiers in an <em>unhygienic</em> (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from <code>unmangle-type-name</code>, but with the lexical context of the original identifier.</p></li><li><p>Finally, we pass the new identifier to <code>type-introducer</code> to properly add the type scope, injecting the fresh binding into the type namespace.</p></li></ol><p>With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write <code>type-out</code> when exporting types, it is unlikely that users will want to write <code>unmangle-types-in</code> around each and every import in their program. For that reason, we can define a slightly modified version of <code>require</code> that implicitly wraps all of its subforms with <code>unmangle-types-in</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="p">(</span><span class="k">rename-out</span><span class="w"> </span><span class="p">[</span><span class="n">require/unmangle</span><span class="w"> </span><span class="k">require</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">require/unmangle</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-types-in</span><span class="w"> </span><span class="n">require-spec</span><span class="p">)</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>…and we’re done. Now, Hackett modules can properly import and export type-level bindings.</p><h3><a name="namespaces-plus-submodules-the-devil-s-in-the-details"></a>Namespaces plus submodules: the devil’s in the details</h3><p>Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of <em>submodules</em>.</p><p>Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.</p><p>Submodules in Racket come in two flavors: <code>module</code> and <code>module*</code>. The difference is what order, semantically, they are defined in. Submodules defined with <code>module</code> are essentially defined <em>before</em> their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with <code>module*</code> are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.</p><p>How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.</p><p>However, there is <a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29">a special sort of <code>module*</code> submodule that uses <code>#f</code> in place of a module language</a>, which gives a module access to <em>all</em> of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a <code>test</code> submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="c1">; not provided</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span> + +<span class="p">(</span><span class="k">module*</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">rackunit</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">check-equal?</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="mi">41</span><span class="p">)</span><span class="w"> </span><span class="mi">42</span><span class="p">))</span></code></pre><p>It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>Remember that <code>make-syntax-introducer</code> is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module <a href="https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29">instantiation</a>, as ensured by Racket’s <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">separate compilation guarantee</a>. This means that each module gets its <em>own</em> pair of scopes. This means the body of a <code>module*</code> submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.</p><p>Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we <em>can</em> preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29"><code>make-syntax-delta-introducer</code></a>, we can create a syntax introducer the adds or removes the <em>difference</em> between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for <code>value-introducer</code> and <code>type-introducer</code> to always operate on the same scopes on each module instantiation:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-value/type-introducers</span> +<span class="w"> </span><span class="n">value-introducer:id</span><span class="w"> </span><span class="n">type-introducer:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">scopeless-id</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">introducer-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">value-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">type-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">type-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-value/type-introducers</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="n">type-introducer</span><span class="p">)</span></code></pre><p>The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. <em>However</em>, code inside <code>begin-for-syntax</code> blocks is still re-evaluated every time the module is instantiated! This means we are <em>not</em> circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.</p><p>We still use <code>make-syntax-introducer</code> to create our two scopes, but critically, we only call <code>make-syntax-introducer</code> inside the <code>define-value/type-introducers</code> macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds <code>value-id</code> and <code>type-id</code> as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use <code>make-syntax-delta-introducer</code> to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.</p><p>By guaranteeing each namespace’s scope is always the same, even for different modules, <code>module*</code> submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.</p><h3><a name="the-final-stretch-making-scribble-documentation-namespace-aware"></a>The final stretch: making Scribble documentation namespace-aware</h3><p>As discussed in <a href="/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/">my previous blog post</a>, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.</p><p>In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!</p><p>Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.</p><p>To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with <code>t:</code> upon import, and they are bound to Scribble <a href="https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29"><em>element transformers</em></a> that indicate they should be typeset without the <code>t:</code> prefix. Fortunately, Scribble’s documentation forms <em>do</em> understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.</p><p>In practice, this means Hackett documentation must now include a proliferation of <code>t:</code> prefixes. For example, here is the code for a typeset REPL interaction:</p><pre><code class="pygments"><span class="n">@</span><span class="p">(</span><span class="n">hackett-examples</span> +<span class="w"> </span><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">square</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">t:-&gt;</span><span class="w"> </span><span class="n">t:Integer</span><span class="w"> </span><span class="n">t:Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">[[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="p">}])</span> +<span class="w"> </span><span class="p">(</span><span class="n">square</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span></code></pre><p>Note the use of <code>t:-&gt;</code> and <code>t:Integer</code> instead of <code>-&gt;</code> and <code>Integer</code>. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.</p><p>This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides <code>deftype</code> and <code>deftycon</code> forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to <code>t:</code>-prefixed identifiers to properly index documented forms. Similarly, <code>defdata</code> and <code>defclass</code> have been updated with an understanding of types.</p><p>The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of <code>defform</code> slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into <code>t:</code>-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.</p><h2><a name="analysis-and-unsolved-problems"></a>Analysis and unsolved problems</h2><p>When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).</p><p>For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom <code>require</code> form, types provided by a module’s <em>language</em> are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike <code>require</code>’s require transformers.</p><p>To circumvent this restriction, <code>#lang hackett</code>’s reader includes a somewhat ad-hoc solution that actually inserts a <code>require</code> into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.</p><p>That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do <em>any</em> special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully <a href="http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html">internalizes extra-linguistic mechanisms</a>.</p><p>As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:</p><ul><li><p><strong>Multi-parameter typeclasses</strong> are implemented, along with <strong>default typeclass method implementations</strong>.</p></li><li><p>Pattern-matching performs basic <strong>exhaustiveness checking</strong>, so unmatched cases are a compile-time error.</p></li><li><p>Hackett ships with a <strong>larger standard library</strong>, including an <code>Either</code> type and appropriate functions, an <code>Identity</code> type, a <code>MonadTrans</code> typeclass, and the <code>ReaderT</code> and <code>ErrorT</code> monad transformers.</p></li><li><p><strong>More things are documented</strong>, and parts of the documentation are slightly improved. Additionally, <strong>Hackett’s internals are much more heavily commented</strong>, hopefully making the project more accessible to new contributors.</p></li><li><p><strong>Parts of the typechecker are dramatically simplified</strong>, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.</p></li><li><p>As always, various bug fixes.</p></li></ul><p>Finally, special mention to two new contributors to Hackett, <a href="https://github.com/iitalics">Milo Turner</a> and <a href="https://github.com/Shamrock-Frost">Brendan Murphy</a>. Also special thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> and <a href="https://github.com/michaelballantyne">Michael Ballantyne</a> for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.</p><ol class="footnotes"><li id="footnote-1"><p>“But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have <code>#lang</code>. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate <code>#lang</code>, not a modified version of Hackett, so Hackett can optimize its user experience for what it <em>is</em>, not what it might be someday. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow <code>*</code> to be used to mean “type” at the kind level and “multiply” at the value level. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, <em>per phase</em>, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Hackett progress report: documentation, quality of life, and snake2017-08-28T00:00:00Z2017-08-28T00:00:00ZAlexis King<article><p>Three months ago, <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">I wrote a blog post describing my new, prototype implementation of my programming language, Hackett</a>. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/"><strong>the first (albeit quite incomplete) approach to Hackett’s documentation</strong></a>.</p><p>I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.</p><h2><a name="a-philosophy-of-documentation"></a>A philosophy of documentation</h2><p>Racket, as a project, has always had <a href="http://docs.racket-lang.org">wonderful documentation</a>. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably <a href="http://docs.racket-lang.org/scribble/index.html">Scribble, the Racket documentation tool</a>. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with <a href="http://docs.racket-lang.org/scribble/plt-manuals.html">a powerful library for documenting Racket code</a>. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.</p><p>Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an <code>@</code> character instead of <code>\</code>. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use <a href="http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29">the provided <code>defproc</code> form</a>:</p><pre><code class="pygments"><span class="n">@defproc</span><span class="p">[(</span><span class="nb">add1</span><span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="nb">number?</span><span class="p">])</span><span class="w"> </span><span class="nb">number?</span><span class="p">]{</span> +<span class="n">Returns</span><span class="w"> </span><span class="n">@racket</span><span class="p">[(</span><span class="nb">+</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="mi">1</span><span class="p">)]</span><span class="o">.</span><span class="p">}</span></code></pre><p>This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into <a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29">something like this</a>:</p><p><a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29"></a></p><p>The fact that Scribble documents are fully-fledged <em>programs</em> equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is <a href="http://docs.racket-lang.org/scribble/eval.html">the <code>scribble/example</code> module</a>, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special <code>eval:check</code> form that enables <a href="https://docs.python.org/3/library/doctest.html">doctest</a>-like checking, which allows documentation to serve double duty as a test suite.</p><p>Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is <em>designed</em> with the goal of supporting many different programming languages, and Scribble is no exception. Things like <a href="http://docs.racket-lang.org/scribble/eval.html"><code>scribble/example</code></a> essentially work out of the box with Hackett, and most of <a href="http://docs.racket-lang.org/scribble/plt-manuals.html"><code>scribble/manual</code></a> can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The <code>defproc</code> and <code>defstruct</code> forms are hardly builtins; they are defined as part of the <code>scribble/manual</code> library in terms of Scribble primitives, and <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt">we can do the same</a>.</p><p>Hackett’s documentation already defines three new forms, <code>defdata</code>, <code>defclass</code>, and <code>defmethod</code>, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the <code>Functor</code> typeclass using <code>defclass</code> like this:</p><pre><code class="pygments"><span class="n">@defclass</span><span class="p">[(</span><span class="n">Functor</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="nb">map</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="p">)})]]{</span> + +<span class="n">A</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">@deftech</span><span class="p">{</span><span class="n">functors</span><span class="p">}</span><span class="o">,</span><span class="w"> </span><span class="n">essentially</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="k">provide</span><span class="w"> </span><span class="n">a</span> +<span class="n">mapping</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">“piercing”</span><span class="w"> </span><span class="n">operation.</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">@racket</span><span class="p">[</span><span class="nb">map</span><span class="p">]</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">viewed</span><span class="w"> </span><span class="n">in</span> +<span class="n">different</span><span class="w"> </span><span class="n">ways:</span> + +<span class="k">...</span><span class="p">}</span></code></pre><p>With only a little more than the above code, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29">Hackett’s documentation includes a beautifully-typeset definition of the <code>Functor</code> typeclass</a>, including examples and rich prose:</p><p><a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29"></a></p><p>Scribble makes Hackett’s documentation shine.</p><h3><a name="a-tale-of-two-users"></a>A tale of two users</h3><p>For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.</p><p>A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.</p><p>However, for experienced users, an authoritative reference is <em>exactly</em> what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.</p><p>This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:</p><ul><li><p>Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.</p><p><a href="https://docs.oracle.com/javase/8/docs/api/">Java’s API documentation</a> documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly <em>small</em> language that does not often change.</p><p>On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java <em>does</em> provide guide-style documentation in the form of the <a href="https://docs.oracle.com/javase/tutorial/">Java Tutorials</a>, but they are of inconsistent quality.</p><p>More importantly, while the Java tutorials link to the API docs, the reverse is <strong>not</strong> true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.</p><p>Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.</p></li><li><p>Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.</p><p>The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is <a href="https://developer.mozilla.org/en-US/">MDN</a>. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.</p><p>The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.</p><p>This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. <a href="http://ramdajs.com/docs/">Ramda’s documentation is nothing but a reference</a>, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, <a href="http://passportjs.org/docs">Passport’s docs are essentially <em>only</em> a set of tutorials</a>, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method <em>does</em>. Fortunately, <a href="https://facebook.github.io/react/docs/hello-world.html">there are some libraries, like React</a>, that absolutely <em>nail</em> this, and they have both styles of documentation that are <strong>actually cross-referenced</strong>. Unfortunately, those are mostly the exceptions, not the norm.</p></li><li><p><a href="https://docs.python.org/3/index.html">Python’s documentation is interesting</a>, since it includes a set of tutorials alongside the API reference, and it <em>also</em> ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on <code>if</code> links to the section in the reference about <code>if</code>, but nothing goes in the other direction, which is something of a missed opportunity.</p></li><li><p><a href="https://hackage.haskell.org/package/base">Haskell manages to be especially bad here</a> (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.</p><p>It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and <a href="https://www.haskell.org/haddock/">Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look</a>.</p></li></ul><p>The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with <em>two</em> pieces of documentation: the <a href="http://docs.racket-lang.org/guide/index.html">Racket Guide</a> and the <a href="http://docs.racket-lang.org/reference/index.html">Racket Reference</a>. The guide includes over <strong>one hundred thousand</strong> words of explanations and examples, and the reference includes roughly <strong>half a million</strong>. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.</p><p>Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the <a href="http://docs.racket-lang.org/hackett/guide.html">Hackett Guide</a> and the <a href="http://docs.racket-lang.org/hackett/reference.html">Hackett Reference</a>, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily <em>need</em> to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a <em>lot</em> more accessible without making it any less useful for power users.</p><h2><a name="rounding-hackett-s-library-sanding-its-edges"></a>Rounding Hackett’s library, sanding its edges</h2><p>One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a <em>lot</em> of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.</p><p>Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:</p><ul><li><p>Hackett includes a <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29"><code>Double</code></a> type for working with IEEE 754 double-precision floating-point numbers.</p></li><li><p>Local definitions are supported via the <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29"><code>let</code></a> and <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29"><code>letrec</code></a> forms.</p></li><li><p>The prelude includes many more functions, especially <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29">functions on lists</a>.</p></li><li><p>The Hackett reader has been adjusted to support using <code>.</code> as a bare symbol, since <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29"><code>.</code> is the function composition operator</a>.</p></li><li><p>The Hackett REPL supports many more forms, including <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29">ADT</a>, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29">class</a>, and <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29">instance</a> definitions. Additionally, the REPL now uses <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a> instances to display the results of expressions. To compensate for the inability to print non-<a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a>able things, a new <code>(#:type expr)</code> syntax is permitted to print the type of <em>any</em> expression.</p></li><li><p>Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.</p></li></ul><p>Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189">heavily commented</a> with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.</p><p>One of the most exciting things about documenting Hackett has been realizing just <em>how much</em> already exists. Seriously, if you have gotten to this point in the blog post but haven’t read <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/">the actual documentation</a> yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for <em>me</em>: it’s a language I’d like to use.</p><p>Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!</p><h2><a name="a-demo-with-pictures"></a>A demo with pictures</h2><p>Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?</p><p>Hackett ships with a special package of demo libraries in the aptly-named <code>hackett-demo</code> package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">the previous Hackett blog post</a>, I demonstrated the capabilities of <code>hackett/demo/web-server</code>. In this blog post, we’re going to use <code>hackett/demo/pict</code> and <code>hackett/demo/pict/universe</code>, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!</p><p>As always, we’ll start with <code>#lang hackett</code>, and we’ll import the necessary libraries:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/pict</span> +<span class="w"> </span><span class="n">hackett/demo/pict/universe</span><span class="p">)</span></code></pre><p>With that, we can start immediately with a tiny example. Just to see how <code>hackett/demo/pict</code> works, let’s start by rendering a red square. We can do this by writing a <code>main</code> action that calls <code>print-pict</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))))</span></code></pre><p>If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!</p><p></p><p>Using the REPL, we can inspect the type of <code>print-pict</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">print-pict</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Unit</span><span class="p">))</span></code></pre><p>Unsurprisingly, displaying a picture to the screen needs <code>IO</code>. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of <code>filled-square</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">filled-square</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">Pict</span><span class="p">)</span></code></pre><p>No <code>IO</code> to be seen! This is because “picts” are entirely <em>pure</em> values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">{(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))</span> +<span class="w"> </span><span class="n">hc-append</span> +<span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))}))</span></code></pre><p>This code will print out a red square to the left of a blue one.</p><p></p><p>Again, <code>hc-append</code> is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">hc-append</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="n">Pict</span><span class="p">))</span></code></pre><p>Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!</p><h3><a name="implementing-a-snake-clone"></a>Implementing a snake clone</h3><p>This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the <code>pict</code> or <code>universe</code> libraries. Hopefully it’s still illustrative.</p><p>We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an <code>Eq</code> instance for our points.</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="n">d:left</span><span class="w"> </span><span class="n">d:right</span><span class="w"> </span><span class="n">d:up</span><span class="w"> </span><span class="n">d:down</span><span class="p">)</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">Eq</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="k">==</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)]</span><span class="w"> </span><span class="p">{{</span><span class="n">a</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">c</span><span class="p">}</span><span class="w"> </span><span class="n">&amp;&amp;</span><span class="w"> </span><span class="p">{</span><span class="n">b</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">d</span><span class="p">}})])</span></code></pre><p>With these two datatypes, we can implement a <code>move</code> function that accepts a point and a direction and produces a new point for an adjacent tile:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Direction</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:left</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:right</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:up</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">})]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:down</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">})])</span></code></pre><p>The next step is to define a type for our world state. The <code>big-bang</code> library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span> +<span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="c1">; snake direction</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; snake blocks</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; food blocks</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">set-ws-direction</span><span class="w"> </span><span class="p">[[</span><span class="n">d</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)])</span></code></pre><p>Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-width</span><span class="w"> </span><span class="mi">50</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-height</span><span class="w"> </span><span class="mi">30</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="p">{(</span><span class="n">d*</span><span class="w"> </span><span class="mf">15.0</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">integer-&gt;double</span><span class="p">})</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="p">(</span><span class="n">blank-rect</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-height</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">13.0</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="n">block</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="n">block</span><span class="p">))</span></code></pre><p>Now we can write our actual <code>render</code> function. To do this, we simply need to render each <code>Point</code> in our <code>World-State</code>’s two lists as a block on an <code>empty-board</code>. We’ll write a helper function, <code>render-on-board</code>, which does exactly that:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render-on-board</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Pict</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">pict</span><span class="w"> </span><span class="n">points</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">foldr</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">acc</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">pict</span><span class="p">))</span> +<span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="n">points</span><span class="p">)])</span></code></pre><p>This function uses <code>foldr</code> to collect each point and place the provided pict at the right location using <code>pin-over</code> on an empty board. Using <code>render-on-board</code>, we can write the <code>render</code> function in just a couple of lines:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)</span> +<span class="w"> </span><span class="mf">0.0</span><span class="w"> </span><span class="mf">0.0</span> +<span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="n">food-points</span><span class="p">))])</span></code></pre><p>Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the <code>big-bang</code> library provides a <code>random-integer</code> function, which we can use to write a <code>random-point</code> action:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">random-point</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">point</span><span class="w"> </span><span class="n">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span> +<span class="w"> </span><span class="n">&lt;*&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-height</span><span class="p">)})</span></code></pre><p>Hackett supports applicative notation using infix operators, so <code>random-point</code> looks remarkably readable. It also runs in <code>IO</code>, since the result is, obviously, random. Fortunately, the <code>on-tick</code> function runs in <code>IO</code> as well (unlike <code>render</code>, which must be completely pure), so we can use <code>random-point</code> when necessary to generate a new food block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">init!</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)})</span> +<span class="w"> </span><span class="p">{</span><span class="nb">reverse</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tail!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="nb">reverse</span><span class="p">})</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">new-snake-point</span><span class="w"> </span><span class="p">(</span><span class="n">move</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">(</span><span class="n">head!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">elem?</span><span class="w"> </span><span class="n">food-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">random-point</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">snake-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">delete</span><span class="w"> </span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">food-points</span><span class="p">)})))</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">init!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)}</span> +<span class="w"> </span><span class="n">food-points</span><span class="p">))))])</span></code></pre><p>This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to <code>new-snake-point</code>, then checks if there is a food block at that location. If there is, it generates a <code>new-food-point</code>, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.</p><p>The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our <code>set-ws-direction</code> function that we wrote earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-key</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">KeyEvent</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:left</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:left</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:right</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:right</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:up</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:up</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:down</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:down</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="k">_</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id</span><span class="p">}])</span></code></pre><p>The <code>on-key</code> function runs in <code>IO</code>, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in <code>pure</code>.</p><p>We’re almost done now—all we need to do is set up the <em>initial</em> state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">initial-state</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">initial-food</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">sequence</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="n">repeat</span><span class="w"> </span><span class="n">random-point</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d:right</span> +<span class="w"> </span><span class="p">{(</span><span class="n">point</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">24</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">23</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">nil</span><span class="p">}</span> +<span class="w"> </span><span class="n">initial-food</span><span class="p">))))</span></code></pre><p>Notably, we can use the <code>repeat</code> function to create an infinite list of <code>random-point</code> actions, <code>take</code> the first five of them, then call <code>sequence</code> to execute them from left to right. Now, all we have to do is put the pieces together in a <code>main</code> block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">state</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">initial-state</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">big-bang</span><span class="w"> </span><span class="n">state</span> +<span class="w"> </span><span class="kd">#:to-draw</span><span class="w"> </span><span class="n">render</span> +<span class="w"> </span><span class="kd">#:on-tick</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="mf">0.2</span> +<span class="w"> </span><span class="kd">#:on-key</span><span class="w"> </span><span class="n">on-key</span><span class="p">)))</span></code></pre><p>And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.</p><p></p><h2><a name="contributing-to-hackett"></a>Contributing to Hackett</h2><p>If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.</p><p>On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, <a href="https://github.com/lexi-lambda/hackett/issues">Hackett has an issue tracker</a>, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.</p><p>Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to <a href="https://github.com/lexi-lambda/hackett/issues">open an issue on GitHub</a>, send me a message on the <code>#racket</code> IRC channel on Freenode, or ping me on <a href="http://racket-slack.herokuapp.com">the Racket Slack team</a>.</p><h2><a name="acknowledgements"></a>Acknowledgements</h2><p>Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to <a href="https://github.com/gelisam">Samuel Gélineau, aka gelisam</a>, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.</p><p>Additionally, I want to specially thank <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.</p><p>Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.</p><ol class="footnotes"></ol></article>User-programmable infix operators in Racket2017-08-12T00:00:00Z2017-08-12T00:00:00ZAlexis King<article><p>Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in <a href="https://github.com/lexi-lambda/hackett">Hackett</a>, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.</p><p>Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done <em>without</em> modifying the stock <code>#lang racket</code> reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.</p><h2><a name="our-mission"></a>Our mission</h2><p>Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we <em>do</em> want:</p><ul><li><p>Infix operators should be user-extensible, not limited to a special set of built-in operators.</p></li><li><p>Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.</p></li><li><p>We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like <code>cons</code>. This allows <code>5 - 1 - 2</code> to be parsed as <code>(- (- 5 1) 2)</code>, but <code>5 :: 1 :: nil</code> to be parsed as <code>(:: 5 (:: 1 nil))</code>.</p></li></ul><p>These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:</p><ul><li><p>We will <strong>not</strong> permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing <code>(+ 1 2)</code> with <code>{1 + 2}</code>.</p></li><li><p>Our implementation will <strong>not</strong> support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.</p></li><li><p>All operators will be binary, and we will <strong>not</strong> support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.</p></li></ul><p>With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"infix.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> + +<span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">{</span><span class="mi">10</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="c1">; =&gt; &#39;(1 7)</span></code></pre><p>Let’s get started.</p><h2><a name="implementing-infix-operators"></a>Implementing infix operators</h2><p>Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:</p><ol><li><p>How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?</p></li><li><p>How can we associate fixity information with certain operators?</p></li></ol><p>We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.</p><p>So, how <em>do</em> we detect if a Racket expression is surrounded by curly braces? Normally, in <code>#lang racket</code>, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated <em>exactly</em> the same as parentheses:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.</p><p>Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29"><em>syntax objects</em></a>. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as <a href="http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29"><em>syntax properties</em></a>. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s <a href="http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29"><em>reader</em></a> (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named <a href="http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29"><code>'paren-shape</code></a>.</p><p>This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="no">#f</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\[</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\{</span></code></pre><p>This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.</p><h3><a name="customizing-application"></a>Customizing application</h3><p>Racket is a language <em>designed</em> to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named <a href="http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29"><code>#%app</code></a>, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>…into this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>What’s special about <code>#%app</code> is that the macroexpander will use whichever <code>#%app</code> is in scope in the expression’s lexical context, so if we write our own version of <code>#%app</code>, it will be used instead of the one from <code>#lang racket</code>. This is what we will use to hook into ordinary Racket expressions.</p><p>To write our custom version of <code>#%app</code>, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the <code>'paren-shape</code> syntax property, <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29"><code>syntax/parse/class/paren-shape</code></a>. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>This code will transform any applications surrounded in curly braces into one that starts with <code>#%infix</code> instead of <code>#%app</code>, so <code>{1 + 2}</code> will become <code>(#%infix 1 + 2)</code>, for example. The identifier <code>#%infix</code> isn’t actually special in any way, it just has a funny name, but we haven’t actually defined <code>#%infix</code> yet, so we need to do that next!</p><p>To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like <code>{1 + 2}</code>, should be converted into the equivalent prefix expressions, in this case <code>(+ 1 2)</code>. We can do this with a simple macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)])</span></code></pre><p>Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the <code>#%app</code> from <code>racket/base</code>, which will avoid any accidental infinite recursion between our <code>#%app</code> and <code>#%infix</code>. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>That’s pretty cool!</p><p>Of course, we probably want to support infix applications with more than just a single binary operator, such as <code>{1 + 2 + 3}</code>. We can implement that just by adding another case to <code>#%infix</code> that handles more subforms:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">10</span></code></pre><p>I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">5</span></code></pre><p>Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as <code>cons</code>, nested <code>-&gt;</code> types or contracts for curried functions, and <code>expt</code>, the exponentiation operator.</p><h3><a name="tracking-operator-fixity"></a>Tracking operator fixity</h3><p>Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.</p><p>Essentially, Racket (like Scheme) uses a <code>define-syntax</code> form to define macros, which is what <code>define-syntax-parser</code> eventually expands into. However, unlike Scheme, Racket’s <code>define-syntax</code> is not <em>just</em> for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Once a binding has been defined using <code>define-syntax</code>, a macro can look up the value associated with it by using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, which returns the compile-time value associated with an identifier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">foo</span><span class="p">)))</span> +<span class="c1">; =&gt; 3</span></code></pre><p>The cool thing is that <code>syntax-local-value</code> gets the value associated with a specific <em>binding</em>, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use <code>syntax-local-value</code> to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the <code>foo</code> binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because <code>foo</code> is not bound to anything at runtime.</p><p>To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the <a href="http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29"><code>prop:procedure</code></a> structure type property.</p><p>If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">))))</span></code></pre><p>This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for <code>runtime-binding</code>, but we can also include a value of our choosing for <code>fixity</code>. Here’s an example:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="nb">cons</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="p">))</span></code></pre><p>This new <code>::</code> binding will act, in every way, just like <code>cons</code>. If we use it in the REPL, you can see that it acts exactly the same:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></code></pre><p>However, we can also use <code>syntax-local-value</code> to extract this binding’s fixity at compile-time, and that’s what makes it interesting:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">::</span><span class="p">))))</span> +<span class="c1">; =&gt; &#39;right</span></code></pre><p>Using this extra compile-time information, we can adjust our <code>#%infix</code> macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used <code>syntax/parse/class/paren-shape</code> to make decisions based on the <code>'paren-shape</code> syntax property, we can use <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29"><code>syntax/parse/class/local-value</code></a> to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span></code></pre><p>Now, we can update <code>#%infix</code> to use our new <code>infix-op</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span></code></pre><p>Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via <code>#:when</code> clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of <code>#%infix</code>, we can successfully use <code>::</code> in an infix expression, and it will be parsed with the associativity that we expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Exciting!</p><h3><a name="a-nicer-interface-for-defining-infix-operators"></a>A nicer interface for defining infix operators</h3><p>We currently have to define infix operators by explicitly using <code>define-syntax</code>, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the <code>define-infix-operator</code> form from the example at the very beginning of this blog post.</p><p>Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the <code>define-syntax</code> definitions we were already writing:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span></code></pre><p>With this in hand, we can define some infix operators with a much nicer syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary <code>#lang racket</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">-1</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">256</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">64</span></code></pre><p>And you know what’s most amazing about this? The entire thing is <strong>only 50 lines of code</strong>. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/local-value</span> +<span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span> +<span class="w"> </span><span class="n">syntax/transformer</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>Racket is a hell of a programming language.</p><h2><a name="applications-limitations-and-implications"></a>Applications, limitations, and implications</h2><p>This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am <em>almost certain</em> that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even <em>locally</em>.</p><p>What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as <code>let-syntax</code> and <code>syntax-parameterize</code>. Using these tools, it would be entirely possible to implement a <code>with-fixity</code> macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make <code>/</code> right associative within a block of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="m">1/6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="nb">/</span><span class="w"> </span><span class="n">right</span><span class="p">])</span> +<span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span> +<span class="mi">1</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of <code>splicing-let</code> and <code>splicing-let-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span> +<span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="n">op:id</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}}]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">op-tmp</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">op</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let</span><span class="w"> </span><span class="p">([</span><span class="n">op-tmp</span><span class="w"> </span><span class="n">op</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">op-tmp</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span></code></pre><p>This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations <em>between</em> operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.</p><p>As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in <a href="https://github.com/lexi-lambda/hackett">Hackett</a> to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow <em>any</em> expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.</p><p>If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt">this file for the definition of infix bindings</a>, as well as <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101">this file for the defintion of infix application</a>. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like <code>{_ - 1}</code> to serve as a shorthand for <code>(lambda [x] {x - 1})</code>, but I haven’t yet decided if I like the tradeoffs involved.</p><p>It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in <code>#lang racket</code> without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.</p><p>Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to <code>#lang hackett</code>, there’s no reason something similar but more powerful couldn’t be built as a separate library by a <em>user</em> of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.</p><ol class="footnotes"></ol></article>Realizing Hackett, a metaprogrammable Haskell2017-05-27T00:00:00Z2017-05-27T00:00:00ZAlexis King<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questions2017-01-05T00:00:00Z2017-01-05T00:00:00ZAlexis King<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheses2017-01-02T00:00:00Z2017-01-02T00:00:00ZAlexis King<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Climbing the infinite ladder of abstraction2016-08-11T00:00:00Z2016-08-11T00:00:00ZAlexis King<article><p>I started programming in elementary school.</p><p>When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to <a href="https://xkcd.com/974/">solve the general problem</a>. When I learned about programming, I was immediately hooked: it was <em>so easy</em> to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.</p><p>Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of <strong>abstraction</strong>.</p><h2><a name="the-brick-wall-of-inexpressiveness"></a>The brick wall of inexpressiveness</h2><p>When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:</p><pre><code class="pygments"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="p">;</span> +<span class="p">}</span> + +<span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span> +<span class="p">}</span></code></pre><p>This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.</p><p>It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The <em>whole reason</em> I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.</p><p>When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.</p><p>Both of these languages were flawed, as all languages are, but they opened my mind to the idea that <em>programming languages</em> could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.</p><h2><a name="discovering-lisp"></a>Discovering Lisp</h2><p>Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.</p><p>This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?</p><p>The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a <a href="https://github.com/lexi-lambda/libsol">highly dynamic Lisp with a prototypal object system called Sol</a>. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.</p><p>Unfortunately, it was also abysmally slow.</p><p>I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called <a href="http://racket-lang.org">Racket</a>. At about the same time, someone pointed me to a totally different language called <a href="https://www.haskell.org">Haskell</a>. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.</p><h2><a name="a-journey-into-complexity"></a>A journey into complexity</h2><p>Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned <em>so much more</em> since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.</p><p>I’ve also started realizing something else, though: <strong>the languages I’ve settled into are <em>really complicated</em>.</strong></p><p>When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.</p><p>Why?</p><p>Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”</p><p>Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?</p><p>I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.</p><p>Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I <em>still</em> have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?</p><h3><a name="improvement-but-never-mastery"></a>Improvement, but never mastery</h3><p>Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.</p><p>For example I am pretty confident that I <em>get</em> JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques <em>within</em> the language, not entirely new linguistic constructs.</p><p>On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about <code>TypeInType</code>? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do <em>not</em> have a good grasp on all the intricacies of Racket’s macro system.</p><p>This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out <code>DataKinds</code>, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.</p><p>Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is <em>terrible</em>, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.</p><p>Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even <em>more</em> advanced things, and still, none of them truly solve my problems.</p><h2><a name="ultimately-it-all-has-at-least-a-little-value"></a>Ultimately, it all has (at least a little) value</h2><p>It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.</p><p>The scariest part of all is that I think it’s completely worthwhile.</p><p>So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs <em>seem</em> unnecessarily complicated, they <em>seem</em> increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.</p><p>The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.</p><p>Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:</p><pre><code class="pygments"><span class="nl">10</span><span class="w"> </span><span class="kr">INPUT</span><span class="w"> </span><span class="s2">"What is your name: "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span> +<span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">"Hello "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span></code></pre><p>On the other hand, very few would probably understand this one:</p><pre><code class="pygments"><span class="c1">-- | A class for categories.</span> +<span class="c1">-- id and (.) must form a monoid.</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">Category</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="c1">-- | the identity morphism</span> +<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="c1">-- | morphism composition</span> +<span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">c</span></code></pre><p>Yet very few new programs are being written in BASIC, and lots are being written in Haskell.</p><p>Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:</p><pre><code class="pygments"><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composeWithProps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">curry</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">childProps</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">omit</span><span class="p">([</span><span class="s1">&#39;children&#39;</span><span class="p">],</span><span class="w"> </span><span class="nx">childProps</span><span class="p">),</span><span class="w"> </span><span class="nx">childProps</span><span class="p">.</span><span class="nx">children</span><span class="p">));</span> +<span class="w"> </span><span class="c1">// give the composed component a pretty display name for debugging</span> +<span class="w"> </span><span class="nx">composed</span><span class="p">.</span><span class="nx">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`Composed(</span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="si">}</span><span class="sb">, </span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">composed</span><span class="p">;</span> +<span class="p">});</span></code></pre><p>Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.</p><p>That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an <em>extremely</em> productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.</p><h3><a name="reactionary-anti-intellectualism-and-the-search-for-moderation"></a>Reactionary anti-intellectualism and the search for moderation</h3><p>I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. <a href="https://twitter.com/lexi_lambda/status/763111451691134976">I recently tweeted a picture of some rather dense mathematics from a paper I’d read</a>, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.</p><p>Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like <code>map</code> should be banned because they are too confusing and not immediately self-explanatory.</p><p>I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.</p><p>Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern <em>technology</em> possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.</p><p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.</p><p>I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.</p><p>Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.</p><ol class="footnotes"></ol></article>Simple, safe multimethods in Racket2016-02-18T00:00:00Z2016-02-18T00:00:00ZAlexis King<article><p>Racket ships with <code>racket/generic</code>, a system for defining <em>generic methods</em>, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports <em>single dispatch</em>. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.</p><h2><a name="motivating-multiple-dispatch"></a>Motivating multiple dispatch</h2><p>What is multiple dispatch and why is it necessary? Well, in most cases, it <em>isn’t</em> necessary at all. <a href="http://dl.acm.org/citation.cfm?doid=1449764.1449808">It has been shown that multiple dispatch is much rarer than single dispatch in practice.</a> However, when actually needed, having multiple dispatch in the toolbox is a valuable asset.</p><p>A classic example of multiple dispatch is multiplication over both scalars and vectors. Ideally, all of the following operations should work:</p><pre><code>2 × 3 = 6 +2 × ⟨3, 4⟩ = ⟨6, 8⟩ +⟨3, 4⟩ × 2 = ⟨6, 8⟩ +</code></pre><p>In practice, most languages do not support such flexible dispatch rules without fairly complicated branching constructs to handle each permutation of input types. Furthermore, since most languages only support single dispatch (such as most object-oriented languages), it is nearly impossible to add support for a new combination of types to an existing method.</p><p>To illustrate the above, even if a language supported operator overloading <em>and</em> it included a <code>Vector</code> class that overloaded multiplication to properly work with numbers and vectors, it might not implement matrix multiplication. If a user defines a <code>Matrix</code> class, they may overload <em>its</em> multiplication to support numbers, vectors, and matrices, but it is impossible to extend the multiplication implementation for the <code>Vector</code> class. That method is now completely set in stone, unless it is edited directly (and the programmer may not have access to <code>Vector</code>’s implementation).</p><p>Multiple dispatch solves all of these problems. Rather than specify implementations of functions for singular types, it is possible to specify implementations for sets of types. In the above example, a programmer would be able to define a new function that operates on <code>Vector</code> and <code>Matrix</code> arguments. Since each definition does not “belong” to any given type, extending this set of operations is trivial.</p><h2><a name="multiple-dispatch-in-racket"></a>Multiple dispatch in Racket</h2><p>This blog post is somewhat long and technical, so before proceeding any further, I want to show some real code that actually works so you can get a feel for what I’m talking about. As a proof-of-concept, I have created <a href="https://github.com/lexi-lambda/racket-multimethod">a very simple implementation of multiple dispatch in Racket</a>. The above example would look like this in Racket using my module:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Pardon the somewhat clunky syntax, but the functionality is there. Using the above code works as expected:</p><pre><code>&gt; (mul (num 2) (num 3)) +(num 6) +&gt; (mul (num 2) (vec '(3 4))) +(vec '(6 8)) +&gt; (mul (vec '(3 4)) (num 2)) +(vec '(6 8)) +</code></pre><p>Making the above snippet work is not particularly hard. In fact, it’s likely that most competent Racketeers could do it without much thought. However, there’s a tiny bit more going on behind the scenes than it may seem.</p><h2><a name="the-problem-with-multiple-dispatch"></a>The problem with multiple dispatch</h2><p>The single-dispatch design limitation of <code>racket/generic</code> comes directly from a desire to avoid what has been described as “spooky action at a distance”, a problem that is prevalent in many systems that support methods with multiple dispatch (aka <em>multimethods</em>). Specifically, the issue arises when new method implementations are defined for existing datatypes, which can have far-reaching effects throughout a program because the method table is global state. Both CLOS and Clojure suffer from this shortcoming.</p><p>Interestingly, Haskell with multi-parameter typeclasses (a nonstandard but highly useful extension) makes it quite trivial to create constructs similar to multiple dispatch (though the overload resolution is done at compile-time). The similarities are significant: Haskell <em>also</em> suffers from the possibility of a certain sort of “spooky action”. However, Haskell’s static typing and resolution allows the compiler to catch these potential issues, known as “orphan instances”, at compile time. Even though Racket does not support the same sort of static typing, the same idea can be used to keep multiple dispatch safe using the macro system.</p><h2><a name="safe-dynamically-typed-multiple-dispatch"></a>Safe, dynamically-typed multiple dispatch</h2><p>In order to make multiple dispatch safe, we first need to determine exactly what is unsafe. Haskell has rules for determining what constitutes an “orphan instance”, and these rules are equally applicable for determining dangerous multimethod implementations. Specifically, a definition can be considered unsafe if <em>both</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented was declared in a different module from the implementation.</p></li><li><p><em>All</em> of the types used for dispatch in the multimethod instance were declared in a different module from the implementation.</p></li></ol><p>Conversely, a multimethod implementation is safe if <em>either</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></li><li><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></li></ol><p>Why do these two rules provide a strong enough guarantee to eliminate the dangers created by global state? Well, to understand that, we need to understand what can go wrong if these rules are ignored.</p><h3><a name="multimethods-and-dangerous-instances"></a>Multimethods and dangerous instances</h3><p>What exactly is this dangerous-sounding “spooky action”, and what causes it? Well, the trouble stems from the side-effectful nature of multimethod instance definitions. Consider the Racket module from earlier, which defines multiplication instances for scalars and vectors:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Note that there is not actually a <code>(mul vec vec)</code> implementation. This is intentional: there are <em>two</em> ways to take the product of two vectors, so no default implementation is provided. However, it is possible that another module might desire an instance for <code>mul</code> that takes the dot product, and the programmer might write the following definition:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">foldl</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">y</span><span class="p">)))))</span></code></pre><p>However, there is something fishy about the above definition: it doesn’t need to be exported with <code>provide</code> to work! Since instances don’t create new bindings, they only add dispatch options, they don’t ever need to <code>provide</code> anything. This is problematic, though: it means that a program could continue to happily compile <em>even if</em> the module containing the dot product instance was never loaded with <code>require</code>, but an attempt to multiply two vectors would fail at runtime, claiming that there was no <code>(mul vec vec)</code> implementation. This drastic change of behavior violates Racket programmers’ assumptions about the guarantees made by modules (<code>require</code> should not cause any side-effects if the module’s bindings are not used).</p><p>Of course, while this seems potentially unexpected, it is workable: just be careful to <code>require</code> modules containing instances. Unfortunately, it gets much worse—what if a different library defines <em>its own</em> <code>(mul vec vec)</code> instance? What if that instance takes the cross product instead? That library may function entirely properly on its own, but when loaded alongside the program that defines a dot product instance, it is impossible to determine which instance should be used where. Because <code>define-instance</code> operates by modifying the aforementioned global state, the implementations clash, and the two systems <em>cannot</em> continue to operate together as written.</p><p>This is pretty bad. Defining extra instances is a reasonable use-case for multiple dispatch, but if these instances can break <em>third-party code</em>, how can they be trusted? This sort of problem can make multiple dispatch difficult to reason about and even more difficult to trust.</p><h3><a name="what-determines-safety"></a>What determines safety?</h3><p>With those problems in mind, we can turn back to the two rules for <em>safe</em> multiple dispatch. How do they prevent the above issues? Well, let’s take them one at a time.</p><p>Remember that an instance can be unequivocally determined to be safe if either of the two conditions are true, so we can consider them entirely independently. The first one is simple—an instance is safe if the following condition holds:</p><blockquote><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></blockquote><p>This one is pretty obvious. It is impossible to create a “bad” instance of a method declared in the same module because it is impossible to import the method without also bringing in the instance. Furthermore, a conflicting instance cannot be defined at the place where the types themselves are defined because that would require a circular module dependency, which Racket does not permit.</p><p>With the above explanation in mind, the second condition should make sense, too:</p><blockquote><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></blockquote><p>The same argument for the first point holds for the second, but with the parties swapped. Again, it is impossible to use the instance without somehow requiring the module that defines the datatype itself, so the instance would always be required, anyway. The most interesting aspect of this condition is that it demonstrates that instances can be defined for existing datatypes (that are defined in other modules) just so long as <em>at least one</em> of the datatypes is defined in the same module. This continues to permit the important use-case of extending the interfaces of existing types.</p><h3><a name="encoding-the-safety-rules-into-racket-s-macro-system"></a>Encoding the safety rules into Racket’s macro system</h3><p>In order to keep track of which methods and instances are defined where, I leveraged a technique based on the one <a href="http://www.ccs.neu.edu/racket/pubs/scheme2007-ctf.pdf">used by Typed Racket to keep track of whether or not a typed identifier is used in a typed or untyped context</a>. However, instead of using a simple mutable boolean flag, I used a mutable <a href="http://docs.racket-lang.org/syntax/syntax-helpers.html#%28tech._identifier._set%29">free identifier set</a>, which keeps track of the identifiers within a given module that should be considered “privileged”.</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/id-set</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mark-id-as-privileged!</span> +<span class="w"> </span><span class="n">id-privileged?</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="p">(</span><span class="n">mutable-free-id-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-add!</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-member?</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span></code></pre><p>Making this work with <code>define-generic</code> is obvious: just invoke <code>mark-id-as-privileged!</code> on the method name to note that the method is “privileged” in the scope of the current module. Keeping track of privileged structs is similarly straightforward, though it is a little more devious: the <code>multimethod</code> module provides a custom <code>struct</code> macro that just expands to <code>struct</code> from <code>racket/base</code>, but adds privilege information.</p><p>The <code>define-instance</code> macro does all the heavy lifting to ensure that only privileged identifiers can be used in instance definitions. A simple check for the identifier annotations is performed before proceeding with macro expansion:</p><pre><code class="pygments"><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">types</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">types</span><span class="p">)))</span></code></pre><p>When the privilege checks fail, an error is raised:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">)))</span></code></pre><p>With the above safeguards in place, the dangerous dot product implementation from above <strong>would not be allowed</strong>. The checks manage to encode both of the safety rules into the macro system such that invalid instances will fail <em>at compile time</em>, preventing dangerous uses of multimethods from ever slipping by unnoticed.</p><h3><a name="actually-implementing-multiple-dispatch"></a>Actually implementing multiple dispatch</h3><p>The rest of the multimethod implementation is relatively straightforward and is not even particularly robust. If anything, it is the bare minimum of what would be needed to allow the safety mechanisms above to work. Lots of features that would likely be needed in a real implementation are not included, and graceful error handling is largely ignored.</p><p>Multimethods themselves are implemented as Racket <a href="http://docs.racket-lang.org/guide/proc-macros.html#%28tech._transformer._binding%29">transformer bindings</a> containing custom data, including a reference to the multimethod’s arity and dispatch table. The custom datatype includes a <code>prop:procedure</code> structure type property, which allows such bindings to also function as macros. The macro procedure expands to an operation that looks up the proper instance to use in the multimethod’s dispatch table and invokes it with the supplied arguments.</p><p>The relevant code for defining multimethods is reproduced below:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="n">arity</span><span class="w"> </span><span class="n">dispatch-table</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="n">method</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">method</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="n">args</span><span class="p">))]))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-generic</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method:id</span><span class="w"> </span><span class="n">arg:id</span><span class="w"> </span><span class="n">...+</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">arity</span><span class="w"> </span><span class="p">(</span><span class="nb">length</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">arg</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="nb">make-hash</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod</span><span class="w"> </span><span class="n">arity</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">dispatch-table</span><span class="p">))))]))</span></code></pre><p>The dispatch tables are implemented entirely in terms of Racket’s structure types, so while they can be defined on arbitrary structure types (including ones defined in the Racket standard library), they <em>cannot</em> be defined on primitives such as pairs or vectors. Implementations are registered in the dispatch table using the compile-time information associated with structs’ transformer bindings, and the same information is retrieved from struct instances at runtime to look up the proper implementation to call. Notably, this only works if the struct is <code>#:transparent</code>, or more generally and accurately, if the calling code has access to the struct’s inspector. All structs defined by the <code>struct</code> form from the <code>multimethod</code> module are automatically marked as <code>#:transparent</code>.</p><p>The following code implements defining multimethod instances:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-instance</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="c1">; standard (define (proc ...) ...) shorthand</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">((</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; full (define proc lambda-expr) notation</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="n">proc:expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod-dispatch-table</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">compose1</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="n">extract-struct-info</span><span class="w"> </span><span class="nb">syntax-local-value</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))])</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">struct-types</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">hash-set!</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="n">struct-types</span><span class="w"> </span><span class="n">proc</span><span class="p">))))]))</span></code></pre><p>The resulting implementation is a useful, if certainly incomplete implementation of multimethods in Racket that does not sacrifice the safety provided by <code>racket/generic</code>’s single-dispatch approach.</p><h2><a name="related-work-advantages-and-disadvantages-and-areas-for-future-improvement"></a>Related work, advantages and disadvantages, and areas for future improvement</h2><p>As previously mentioned, this implementation of multiple dispatch was inspired by the types of APIs offered by CLOS and Clojure while also maintaining the safety of <code>racket/generic</code>. The inspiration for the safety rules came from GHC’s detection of orphan instances. Although most of the ideas presented above exist in other places, I am unsure if the concept of safety checking has been used before in any dynamically-typed programming languages.</p><p>The primary advantage offered over Racket’s existing generics system is obvious: multiple dispatch. Furthermore, this system can supersede many uses of <code>racket/generic</code> simply by dispatching on a single type. However, the current implementation does <em>not</em> support all of the features of <code>racket/generic</code>, such as supporting non-structure types and allowing fallback implementations. While those are well within the realm of possibility, other things like attaching structure type properties are probably not possible with this approach, so it is unlikely that the existing system could be subsumed by one like this one.</p><p>Additionally, this implementation would almost certainly need numerous improvements before being useful to most programmers:</p><ul><li><p><strong>Good error reporting for failure cases.</strong> Right now, even something obvious like calling a method on values that do not implement it simply fails with an error produced by <code>hash-ref</code>. In a more interesting sense, using the arity to generate compile-time error messages for <code>define-instance</code> would be a nice improvement.</p></li><li><p><strong>Support for Racket primitive data types.</strong> This might require some cooperation from Racket itself to permit an elegant implementation, but they could also just be special-cased. So long as lookup for primitives was done <em>after</em> consulting the main dispatch table, there wouldn’t be any performance hit for non-primitive types.</p></li><li><p><strong>Option to supply fallback implementations.</strong> This wouldn’t be too hard at all, though it’s questionable whether or not it would be useful without method groupings like <code>define/generic</code> provides. There would likely also need to be some sort of way to check if a set of values implements a particular method.</p></li><li><p><strong>Better cooperation with structure inspectors to alleviate the need for all structures to be transparent.</strong> It’s currently unclear to me how exactly this works and how it <em>should</em> work. There might be a better way to do this without mucking with inspectors.</p></li><li><p><strong>Much more flexible argument lists, including the ability to specify arguments that are not used for dispatch.</strong> This is really a pretty fundamental requirement, but the parsing required was significant enough for me to put it off for this initial prototype.</p></li><li><p><strong>Scribble forms to document generic methods and their instances.</strong> This is something <code>racket/generic</code> <em>doesn’t</em> have, and it has suffered for it. It would be very nice to have easy documentation forms for multimethods.</p></li><li><p><strong>Proper consideration of struct subtyping.</strong> Racket structs support subtyping, which I have not given much thought for this prototype. It is possible that subtyping violates constraints I had assumed would hold, so reviewing the existing code with that context would be useful.</p></li></ul><p>I’m not sure how much effort is involved in most of the above ideas, and in fact I’m not even completely sure how useful this system is to begin with. I have not found myself reaching much for multiple dispatch in my time as a Racket programmer, but that could simply be because it was previously unavailable. It will be interesting to see if that changes now that I have built this system, even if it is a bit rough around the edges.</p><h2><a name="conclusion"></a>Conclusion</h2><p>Despite the lack of need for multiple dispatch to solve most problems, as indicated by its general lack of support in mainstream programming languages, it’s a nice tool to have in the toolbox, and it <em>is</em> asked for in the Racket community from time to time (perhaps due to its familiarity in other parts of the Lisp world). Time will tell if pointing people to something like this will create or stifle interest in multiple dispatch for Racket.</p><p>The source for the <a href="https://github.com/lexi-lambda/racket-multimethod"><code>multimethod</code> package can be found here</a> if you are at all interested in playing with it yourself.</p><ol class="footnotes"></ol></article>ADTs in Typed Racket with macros2015-12-21T00:00:00Z2015-12-21T00:00:00ZAlexis King<article><p>Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate <em>why</em> macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is <strong>ADTs</strong>.</p><h2><a name="warning-this-is-not-a-macro-tutorial"></a>Warning: this is not a macro tutorial</h2><p>First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you <em>are</em> interested in learning about macros, I must recommend Greg Hendershott's <a href="http://www.greghendershott.com/fear-of-macros/">Fear of Macros</a>. It is good. This is not that.</p><p>Now, with that out of the way, let's get started.</p><h2><a name="what-we-re-building"></a>What we&rsquo;re building</h2><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data types</a>, or <em>ADTs</em>, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.</p><p>Racket also already has a facility for creating custom data structures in the form of <em>structs</em>, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.</p><p>With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="kt">Tree</span></code></pre><p>This already demonstrates a few of the core things we'll need to build:</p><ol><li><p>Each ADT has a <em>data type</em>, in this case <code>Tree</code>. This name only exists in the world of types, it isn't a value.</p></li><li><p>Each ADT has various <em>data constructors</em>, in this case <code>Leaf</code> and <code>Node</code>.</p></li><li><p>Each data constructor may accept any number of arguments, each of which have a specific type.</p></li><li><p>The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.</p></li></ol><p>Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>With this in mind, we can add a fifth and final point to our list:</p><ol start="5"><li><p>ADTs must be able to be parametrically polymorphic.</p></li></ol><p>That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.</p><h3><a name="describing-adts-in-racket"></a>Describing ADTs in Racket</h3><p>How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>This looks pretty good. Just like with the Haskell implementation, <code>Tree</code> should only exist at the type level, and <code>Empty</code>, <code>Leaf</code>, and <code>Node</code> should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be <code>(Leaf 7)</code>.</p><p>Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's <code>match</code>[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">tree-sum</span><span class="w"> </span><span class="p">((</span><span class="n">Tree</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">tree</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">tree</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Node</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">l</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">r</span><span class="p">))]))</span></code></pre><p>Given that Racket's <code>struct</code> form automatically produces identifiers that cooperate with <code>match</code>, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.</p><h2><a name="implementing-adts-as-syntax"></a>Implementing ADTs as syntax</h2><p>Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. The <code>syntax/parse</code> library works like the traditional Scheme <code>syntax-case</code> on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.</p><p>Since this is not a macro tutorial, the following implementation assumes you already know how to use <code>syntax/parse</code>. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.</p><h3><a name="parsing-types-with-a-syntax-class"></a>Parsing types with a syntax class</h3><p>To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like <code>Tree</code>, or they can be identifiers with parameters, like <code>(Tree a)</code>. We'll want to cover both cases.</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">))))</span></code></pre><p>This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (<code>...+</code>) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use <code>#:attr</code> to give the <code>param</code> attribute the default value of an empty list, so this syntax class will actually <em>normalize</em> the input we get in addition to actually parsing it.</p><h3><a name="a-first-attempt-at-define-datatype"></a>A first attempt at <code>define-datatype</code></h3><p>Now we can move on to actually implementing <code>define-datatype</code>. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using <code>syntax-parser</code>, which actually does the parsing for our macro.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">]))</span></code></pre><p>This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with <code>_</code> (which will just be the identifier <code>define-datatype</code>), then expecting a <code>type-name</code>, which uses the <code>type</code> syntax class we defined above. Next, we expect zero or more <code>data-constructor</code>s, which also use the <code>type</code> syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.</p><p>Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">([</span><span class="n">f</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))]))</span></code></pre><p>This is actually really close to being correct. This will generate a struct definition for each <code>data-constructor</code>, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have <em>names</em>, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for <em>all</em> the fields, <code>f</code>, so if any data constructor has two or more fields, we'll get an error.</p><p>Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called <code>generate-temporary</code>, which generates random identifiers. Our next attempt might look like this:</p><pre><code class="pygments"><span class="o">#`</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="o">#,</span><span class="p">(</span><span class="n">generate-temporary</span><span class="p">)</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>The <code>#,</code> lets us "escape" from the template to execute <code>(generate-temporary)</code> and interpolate its result into the syntax. Unfortunately, this doesn't work. We <em>do</em> generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.</p><h3><a name="more-leveraging-syntax-classes"></a>More leveraging syntax classes</h3><p>As it turns out, this is <em>also</em> easy to do with syntax classes. We can add an extra attribute to our <code>type</code> syntax class to generate a random identifier with each one. Again, we can use <code>#:attr</code> to do that automatically. Our new definition for <code>type</code> will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span></code></pre><p>Here we're using <code>generate-temporaries</code> instead of <code>generate-temporary</code>, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each <code>param</code>.</p><p>We can now fix our macro to use this <code>field-id</code> attribute instead of the static field name:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><h3><a name="creating-the-supertype"></a>Creating the supertype</h3><p>We're almost done—now we just need to implement our overall type, the one defined by <code>type-name</code>. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">Tree</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">Leaf</span><span class="w"> </span><span class="n">Node</span><span class="p">))</span></code></pre><p>However, a polymorphic type alias would need to include the type parameters in each subtype, like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using <strong>procedural macros</strong>.</p><p>To build each properly-instantiated type, we'll use a combination of <code>define/with-syntax</code> and Racket's list comprehensions, <code>for/list</code>. The <code>define/with-syntax</code> form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by <code>syntax-parser</code>. This will allow us to break up our result into multiple steps. Technically, <code>define/with-syntax</code> is not strictly necessary—we could just use <code>#`</code> and <code>#,</code>—but it's cleaner to work with.</p><p>We'll start by defining a set of instantiated data constructor types, one per <code>data-constructor</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span></code></pre><p>Now with our definition for <code>data-type</code>, we can implement our type alias for the supertype extremely easily:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><h3><a name="putting-it-all-together"></a>Putting it all together</h3><p>There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by <code>type-name</code> are in scope for each data constructor's structure definition. We can do this by making use of <code>type-name.param</code> within each produced struct definition, resulting in this:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><p>And we're done! The final macro, now completed, looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span> + +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span><span class="w"> </span><span class="k">...</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">)))]))</span></code></pre><p>It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.</p><h2><a name="using-our-adts"></a>Using our ADTs</h2><p>With the macro built, we can now actually use our ADTs using the syntax we described! The following is now <em>valid code</em>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span> +<span class="nb">-</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">Positive-Byte</span><span class="p">)</span> +<span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span></code></pre><p>We can use this to define common data types, such as Haskell's <code>Maybe</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Nothing</span><span class="p">)</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-default</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-default</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-then</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-then</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Nothing</span><span class="p">)]))</span></code></pre><p>And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="n">Expr</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="n">Number</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Expr</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Number</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">e</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Value</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Add</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">-</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Divide</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">/</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">7</span><span class="p">))))</span> +<span class="mi">4</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, <a href="https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e">I've put together a gist here</a>.</p><h2><a name="conclusions-and-credit"></a>Conclusions and credit</h2><p>This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly <em>are</em> powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.</p><p>This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.</p><p>That said, I think it's pretty cool.</p><p>Finally, I must give credit where credit is due. Thanks to <a href="http://andmkent.com">Andrew M. Kent</a> for the creation of the <a href="https://github.com/andmkent/datatype">datatype</a> package, which served as the inspiration for this blog post. Many thanks to <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> and <a href="http://www.ccs.neu.edu/home/matthias/">Matthias Felleisen</a> for their work on creating <code>syntax/parse</code>, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.</p><p>Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.</p><ol class="footnotes"></ol></article>Managing application configuration with Envy2015-08-30T00:00:00Z2015-08-30T00:00:00ZAlexis King<article><p>Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, <a href="http://12factor.net">The Twelve-Factor App</a> provides a set of standards for keeping web apps sane, and <a href="http://12factor.net/config">one of those guidelines advises keeping configuration in the environment</a>.</p><p><a href="https://github.com/lexi-lambda/envy">Envy</a> is the declarative bridge between Racket code and the outside world of the environment.</p><h2><a name="introducing-envy"></a>Introducing Envy</h2><p>I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require <code>envy</code> and you're good to go.</p><p>The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:</p><pre><code class="pygments"><span class="c1">; environment.rkt</span> +<span class="kn">#lang </span><span class="nn">typed/racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">envy</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/provide-environment</span> +<span class="w"> </span><span class="n">api-token</span> +<span class="w"> </span><span class="p">[</span><span class="n">log-level</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="kd">#:default</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">info</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">parallel?</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Boolean</span><span class="p">])</span></code></pre><p>When this module is required, Envy will automatically do the following:</p><ol><li><p>Envy will check the values of three environment variables: <code>API_TOKEN</code>, <code>LOG_LEVEL</code>, and <code>PARALLEL</code>.</p></li><li><p>If either <code>API_TOKEN</code> or <code>PARALLEL</code> is not set, an error will be raised:</p><pre><code>envy: The required environment variable "API_TOKEN" is not defined. +</code></pre></li><li><p>The values for <code>LOG_LEVEL</code> and <code>PARALLEL</code> will be parsed to match their type annotations.</p></li><li><p>If <code>LOG_LEVEL</code> is not set, it will use the default value, <code>'info</code>.</p></li><li><p>The values will be stored in <code>api-token</code>, <code>log-level</code>, and <code>parallel?</code>, all of which will be provided by the enclosing module.</p></li></ol><p>Now just <code>(require (prefix-in env: "environment.rkt"))</code>, and the environment variables are guaranteed to be available in your application's code.</p><h2><a name="working-with-typed-racket"></a>Working with Typed Racket</h2><p>As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, <code>define/provide-environment</code> will <em>only</em> work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.</p><p>However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.</p><pre><code>&gt; parallel? +- : Boolean +#t +</code></pre><p>Envy really shines when using optional environment variables with the <code>#:default</code> option. The type of the value given to <code>#:default</code> doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.</p><pre><code>&gt; (define-environment + [num-threads : Positive-Integer #:default #f]) +&gt; num-threads +- : (U Positive-Integer #f) +#f +</code></pre><p>This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.</p><h2><a name="and-more"></a>And more...</h2><p>To see the full set of features that Envy already provides, <a href="https://lexi-lambda.github.io/envy/envy.html">take a look at the documentation</a>. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, <a href="https://github.com/lexi-lambda/envy/issues">open an issue and make a suggestion</a>! I already have plans for a <code>#lang envy</code> DSL, which will hopefully cut the boilerplate out in its entirety.</p><p>And finally, to give credit where credit is due, Envy is heavily inspired by <a href="https://github.com/eval/envied">Envied</a> (both in name and function), an environment variable manager for Ruby, which I've used to great effect.</p><p>Try it out!</p><ul><li><p><code>raco pkg install envy</code></p></li><li><p><a href="https://github.com/lexi-lambda/envy">Envy on GitHub</a></p></li><li><p><a href="https://lexi-lambda.github.io/envy/envy.html">Envy documentation</a></p></li></ul><ol class="footnotes"></ol></article>Deploying Racket applications on Heroku2015-08-22T00:00:00Z2015-08-22T00:00:00ZAlexis King<article><p><a href="https://www.heroku.com">Heroku</a> is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.</p><h2><a name="building-the-server"></a>Building the server</h2><p>Racket's <a href="http://docs.racket-lang.org/web-server/index.html">web-server</a> package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">web-server/servlet</span> +<span class="w"> </span><span class="n">web-server/servlet-env</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="n">req</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">response/xexpr</span> +<span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="ss">html</span><span class="w"> </span><span class="p">(</span><span class="ss">head</span><span class="w"> </span><span class="p">(</span><span class="ss">title</span><span class="w"> </span><span class="s2">"Racket Heroku App"</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="ss">body</span><span class="w"> </span><span class="p">(</span><span class="ss">h1</span><span class="w"> </span><span class="s2">"It works!"</span><span class="p">)))))</span> + +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span></code></pre><p>Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the <code>PORT</code> environment variable. We can access this using the Racket <code>getenv</code>[racket] function.</p><p>Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass <code>#f</code> for the <code>#:listen-ip</code> argument.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;number</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">))</span> +<span class="w"> </span><span class="mi">8080</span><span class="p">))</span> +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span><span class="p">)</span></code></pre><p>Also, by default, <code>serve/servlet</code>[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.</p><pre><code class="pygments"><span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span> +<span class="w"> </span><span class="kd">#:command-line?</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span></code></pre><p>That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.</p><h2><a name="setting-up-our-app-for-heroku"></a>Setting up our app for Heroku</h2><p>The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:</p><pre><code class="pygments">$<span class="w"> </span>git<span class="w"> </span>init +$<span class="w"> </span>heroku<span class="w"> </span>git:remote<span class="w"> </span>-a<span class="w"> </span>racket-heroku-sample +$<span class="w"> </span>heroku<span class="w"> </span>buildpacks:set<span class="w"> </span>https://github.com/lexi-lambda/heroku-buildpack-racket</code></pre><p>We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the <code>RACKET_VERSION</code> environment variable as follows:</p><pre><code class="pygments">$<span class="w"> </span>heroku<span class="w"> </span>config:set<span class="w"> </span><span class="nv">RACKET_VERSION</span><span class="o">=</span><span class="m">6</span>.2.1</code></pre><p>Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.</p><p>Specifically, we just want to run our <code>serve.rkt</code> module. The Racket buildpack installs the repository as a package, so we can run <code>racket</code> with the <code>-l</code> flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:</p><pre><code>web: racket -l sample-heroku-app/server +</code></pre><p>Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can <a href="https://racket-heroku-sample.herokuapp.com">navigate to our app's URL and actually see it running live</a>!</p><h2><a name="conclusion"></a>Conclusion</h2><p>That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.</p><p>That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can <a href="https://github.com/lexi-lambda/racket-sample-heroku-app">find it on GitHub here</a>. For more details on the buildpack itself, <a href="https://github.com/lexi-lambda/heroku-buildpack-racket">it's also available on GitHub here</a>.</p><ol class="footnotes"></ol></article>Automatically deploying a Frog-powered blog to GitHub pages2015-07-18T00:00:00Z2015-07-18T00:00:00ZAlexis King<article><p>So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses <a href="http://www.greghendershott.com">Greg Hendershott</a>'s fantastic <a href="https://github.com/greghendershott/frog">Frog</a> tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via <a href="https://travis-ci.org">Travis CI</a>, so my blog is always up-to-date.</p><h2><a name="setting-up-frog"></a>Setting up Frog</h2><p>I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple <code>raco pkg install frog</code> followed by <code>raco frog --init</code> and <code>raco frog -bp</code> created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is <code>raco frog -n "Post Title"</code>, and you're good to go.</p><p>By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use <a href="http://sass-lang.com">Sass</a> for my stylesheets, potentially with support for <a href="http://coffeescript.org">CoffeeScript</a> later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used <a href="http://gulpjs.com">Gulp</a> in conjunction with <a href="https://www.npmjs.com">NPM</a> for build and dependency management.</p><p>Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.</p><h2><a name="configuring-automatic-deployment-with-travis"></a>Configuring automatic deployment with Travis</h2><p>Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being <a href="https://gist.github.com/domenic/ec8b0fc8ab45f39403dd">this Gist</a>, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special <code>gh-pages</code> branch.</p><p>To make this easy, Frog can be configured to output to a separate directory via the <code>.frogrc</code> configuration file. I chose to output to the <code>out</code> directory:</p><pre><code>output-dir = out +</code></pre><p>I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.</p><pre><code>$ cd out +$ git init +$ git add . +$ git commit -m "Deploy to GitHub Pages" +$ git push --force "$REMOTE_URL" master:gh-pages +</code></pre><p>The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's <a href="http://docs.travis-ci.com/user/encryption-keys/">encryption keys</a> along with a GitHub <a href="https://github.com/settings/tokens">personal access token</a>. Just install the Travis CLI client, copy the access token, and run a command:</p><pre><code>$ gem install travis +$ travis encrypt GH_TOKEN=&lt;access token...&gt; +</code></pre><p>The output of that command is an encrypted value to be placed in an environment variable in the project's <code>.travis.yml</code> configuration file. The URL for the repository on GitHub will also need to be specified as well:</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span></code></pre><p>Now all that's left is configuring the <code>.travis.yml</code> to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to <code>python</code>, then installed Racket and Frog as pre-installation steps.</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span></code></pre><p>(It might be worth noting that Greg Hendershott <em>also</em> maintains the repository that contains the above Travis build script!)</p><p>Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses <code>master</code>, not <code>gh-pages</code>. Obviously, I didn't want Travis running on my <code>master</code> branch, since it would be deploying to that, so I added a branch whitelist:</p><pre><code class="pygments"><span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span></code></pre><p>All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:</p><pre><code class="pygments"><span class="ch">#!/bin/bash</span> +<span class="nb">set</span><span class="w"> </span>-ev<span class="w"> </span><span class="c1"># exit with nonzero exit code if anything fails</span> + +<span class="c1"># clear the output directory</span> +rm<span class="w"> </span>-rf<span class="w"> </span>out<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">0</span><span class="p">;</span> + +<span class="c1"># build the blog files + install pygments for highlighting support</span> +npm<span class="w"> </span>install +npm<span class="w"> </span>run<span class="w"> </span>build +pip<span class="w"> </span>install<span class="w"> </span>pygments +raco<span class="w"> </span>frog<span class="w"> </span>--build + +<span class="c1"># go to the out directory and create a *new* Git repo</span> +<span class="nb">cd</span><span class="w"> </span>out +git<span class="w"> </span>init + +<span class="c1"># inside this git repo we&#39;ll pretend to be a new user</span> +git<span class="w"> </span>config<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Travis CI"</span> +git<span class="w"> </span>config<span class="w"> </span>user.email<span class="w"> </span><span class="s2">"&lt;your@email.here&gt;"</span> + +<span class="c1"># The first and only commit to this new Git repo contains all the</span> +<span class="c1"># files present with the commit message "Deploy to GitHub Pages".</span> +git<span class="w"> </span>add<span class="w"> </span>. +git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"Deploy to GitHub Pages"</span> + +<span class="c1"># Force push from the current repo&#39;s master branch to the remote</span> +<span class="c1"># repo. (All previous history on the branch will be lost, since we are</span> +<span class="c1"># overwriting it.) We redirect any output to /dev/null to hide any sensitive</span> +<span class="c1"># credential data that might otherwise be exposed.</span> +git<span class="w"> </span>push<span class="w"> </span>--force<span class="w"> </span>--quiet<span class="w"> </span><span class="s2">"https://</span><span class="si">${</span><span class="nv">GH_TOKEN</span><span class="si">}</span><span class="s2">@</span><span class="si">${</span><span class="nv">GH_REF</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>master<span class="w"> </span>&gt;<span class="w"> </span>/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span></code></pre><p>For reference, my final <code>.travis.yml</code> looked like this:</p><pre><code class="pygments"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span> +<span class="nt">python</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&#39;3.4&#39;</span> + +<span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span> + +<span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/lexi-lambda/lexi-lambda.github.io.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;long secure token...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span> + +<span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bash ./deploy.sh</span></code></pre><p>That's it! Now I have a working blog that I can publish just by pushing to the <code>source</code> branch on GitHub.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/racket.rss.xml b/feeds/racket.rss.xml new file mode 100644 index 0000000..1c64404 --- /dev/null +++ b/feeds/racket.rss.xml @@ -0,0 +1,1691 @@ +Posts tagged ‘racket’ | Alexis King’s BlogPosts tagged ‘racket’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/racket.html21 Apr 201921 Apr 201960Defeating Racket’s separate compilation guaranteehttps://lexi-lambda.github.io/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/https://lexi-lambda.github.io/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/21 Apr 2019<article><p>Being a self-described <a href="https://felleisen.org/matthias/manifesto/sec_pl-pl.html">programming-language programming language</a> is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate <a href="https://www.cs.utah.edu/plt/publications/macromod.pdf">composable and compilable macros</a>. One of the module system’s foundational properties is its <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29"><em>separate compilation guarantee</em></a>, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is <em>essential</em> for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.</p><p>Yet today, in this blog post, we’re going to do exactly that.</p><h2><a name="what-is-the-separate-compilation-guarantee"></a>What is the separate compilation guarantee?</h2><p>Before we get to the fun part (i.e. breaking things), let’s go over some fundamentals so we understand what we’re breaking. The authoritative source for the separate compilation guarantee is <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">the Racket reference</a>, but it is dense, as authoritative sources tend to be. Although I enjoy reading technical manuals for sport, it is my understanding that not all the people who read this blog are as strange as I am, so let’s start with a quick primer, instead. (If you’re already an expert, feel free to <a href="#section:main-start">skip to the next section</a>.)</p><p>Racket is a macro-enabled programming language. In Racket, a macro is a user-defined, code-to-code transformation that occurs at compile-time. These transformations cannot make arbitrary changes to the program—in Racket, they are usually required to be <em>local</em>, affecting a single expression or definition at a time—but they may be implemented using arbitrary code. This means that a macro can, if it so desires, read the SSH keys off your filesystem and issue an HTTP request to send them someplace.</p><p>That kind of attack is bad, admittedly, but it’s also <em>uninteresting</em>: Racket allows you do all that and then some, making no attempt to prevent it.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Racket calls these “external effects,” things that affect state outside of the programming language. They sound scary, but in practice, <em>internal effects</em>—effects that mutate state inside the programming language—are a much bigger obstacle to practical programming. Let’s take a look at why.</p><p>Let’s say we have a module with some global, mutable state. Perhaps it is used to keep track of a set of delicious foods:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><p>Using this interface, let’s write a program that checks if a particular food, given as a command-line argument, is delicious:</p><pre><code class="pygments"><span class="c1">;; check-food.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"pineapple"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"sushi"</span><span class="p">)</span> +<span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="s2">"cheesecake"</span><span class="p">)</span> + +<span class="p">(</span><span class="k">command-line</span> +<span class="w"> </span><span class="kd">#:args</span><span class="w"> </span><span class="p">[</span><span class="n">food-to-check</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"~a is not delicious.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">food-to-check</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>cheesecake +cheesecake<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span>licorice +licorice<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>Exhilarating. (Sorry, licorice fans.) But what if a <em>macro</em> were to call <code>add-delicious-food!</code>? What would happen? For example, what if we wrote a macro to add a lot of foods at once?<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="n">fst:string</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd:string</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">for*</span><span class="w"> </span><span class="p">([</span><span class="n">fst-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">fst</span><span class="w"> </span><span class="k">...</span><span class="p">]))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">snd-str</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;datum</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">snd</span><span class="w"> </span><span class="k">...</span><span class="p">]))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="p">(</span><span class="nb">string-append</span><span class="w"> </span><span class="n">fst-str</span><span class="w"> </span><span class="s2">" "</span><span class="w"> </span><span class="n">snd-str</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">))</span> + +<span class="c1">; should add “fried chicken,” “roasted chicken”, “fried potato,” and “roasted potato”</span> +<span class="p">(</span><span class="n">add-food-combinations!</span><span class="w"> </span><span class="p">[</span><span class="s2">"fried"</span><span class="w"> </span><span class="s2">"roasted"</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="s2">"chicken"</span><span class="w"> </span><span class="s2">"potato"</span><span class="p">])</span></code></pre><p>Now, what do you think executing <code>racket check-food.rkt 'fried chicken'</code> will do?</p><p>Clearly, the program should print <code>fried chicken is a delicious food</code>, and indeed, many traditional Lisp systems would happily produce such a result. After all, running <code>racket check-food.rkt 'fried chicken'</code> must load the source code inside <code>check-food.rkt</code>, expand and compile it, then run the result. While the program is being expanded, the compile-time calls to <code>add-delicious-food!</code> should add new elements to the <code>delicious-food</code> set, so when the program is executed, the string <code>"fried chicken"</code> ought to be in it.</p><p>But if you actually try this yourself, you will find that <em>isn’t</em> what happens. Instead, Racket rejects the program:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +check-food.rkt:12:11:<span class="w"> </span>add-delicious-food!:<span class="w"> </span>reference<span class="w"> </span>to<span class="w"> </span>an<span class="w"> </span>unbound<span class="w"> </span>identifier +<span class="w"> </span>at<span class="w"> </span>phase:<span class="w"> </span><span class="m">1</span><span class="p">;</span><span class="w"> </span>the<span class="w"> </span>transformer<span class="w"> </span>environment +<span class="w"> </span><span class="k">in</span>:<span class="w"> </span>add-delicious-food!</code></pre><p>Why does Racket reject this program? Well, consider that Racket allows programs to be pre-compiled using <code>raco make</code>, doing all the work of macroexpansion and compilation to bytecode ahead of time. Subsequent runs of the program will use the pre-compiled version, without having to run all the macros again. This is a problem, since expanding the <code>add-food-combinations!</code> macro had side-effects that our program depended on!</p><p>If Racket allowed the above program, it might do different things depending on whether it was pre-compiled. Running directly from source code might treat <code>'fried chicken'</code> as a delicious food, while running from pre-compiled bytecode might not. Racket considers this unacceptable, so it disallows the program entirely.</p><h3><a name="preserving-separate-compilation-via-phases"></a>Preserving separate compilation via phases</h3><p>Hopefully, you are now mostly convinced that the above program is a bad one, but you might have some lingering doubts. You might, for example, wonder if Racket disallows mutable compile-time state entirely. That is not the case—Racket really does allow everything that happens at runtime to happen at compile-time—but it does prevent compile-time and run-time state from ever <em>interacting</em>. Racket stratifies every program into a compile-time part and a run-time part, and it restricts communication between them to limited, well-defined channels (mainly via expanding to code that does something at run-time).</p><p>Racket calls this system of stratification <em>phases</em>. Code that executes at run-time belongs to the <em>run-time phase</em>, while code that executes at compile-time (i.e. macros) belongs to the <em>compile-time phase</em>. When a variable is defined, it is always defined in a particular phase, so bindings declared with <code>define</code> can only be used at run-time, while bindings declared with <code>define-for-syntax</code> can only be used at compile-time. Since <code>add-delicious-food!</code> was declared using <code>define</code>, it was not allowed (and in fact was not even visible) in the body of the <code>add-food-combinations!</code> macro.</p><p>While the whole macro system could work precisely as just described, such a strict stratification would be incredibly rigid. Since every definition would belong to either run-time or compile-time, but never both, reusing run-time code to implement macros would be impossible. While the example in the previous section might make it seem like that’s a good thing, it very often isn’t: imagine if general-purpose functions like <code>map</code> and <code>filter</code> all needed to be written twice!</p><p>To avoid this problem, Racket allows modules to be imported at both run-time and compile-time, so long as it’s done explicitly. Writing <code>(require "some-library.rkt")</code> requires <code>some-library.rkt</code> for run-time code, but writing <code>(require (for-syntax "some-library.rkt"))</code> requires it for compile-time code. Requiring a module <code>for-syntax</code> is sort of like implicitly adjusting all of its uses of <code>define</code> to be <code>define-for-syntax</code>, instead, effectively shifting all the code from run-time to compile-time. This kind of operation is therefore known as <em>phase shifting</em> in Racket terminology.</p><p>We can use phase shifting to make the program we wrote compile. If we adjust the <code>require</code> at the beginning of our program, then we can ensure <code>add-delicious-food!</code> is visible to both the run-time and compile-time parts of <code>check-food.rkt</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="s2">"foods.rkt"</span><span class="p">))</span></code></pre><p>Now our program compiles. However, if you’ve been following everything carefully, you should be wondering why! According to the last section, sharing state between run-time and compile-time fundamentally can’t work without introducing inconsistencies between uncompiled and pre-compiled code. And that’s true—such a thing would cause all sorts of problems, and Racket doesn’t allow it. If you run the program, whether pre-compiled or not, you’ll find it always does the same thing:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>This seems rather confusing. What happened to the calls to <code>add-delicious-food!</code> inside our <code>add-food-combinations!</code> macro? If we stick a <code>printf</code> inside <code>add-delicious-food!</code>, we’ll find that it really does get called:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"Registering ~a as a delicious food.</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And in fact, if we pre-compile <code>check-food.rkt</code>, we’ll see that the first four registrations appear at compile-time, exactly as we expect:</p><pre><code class="pygments">$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +Registering<span class="w"> </span>fried<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>fried<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>chicken<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>roasted<span class="w"> </span>potato<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +Registering<span class="w"> </span>pineapple<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>sushi<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +Registering<span class="w"> </span>cheesecake<span class="w"> </span>as<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>The compile-time registrations really are happening, but Racket is automatically restricting the compile-time side-effects so they only apply at compile-time. After compilation has finished, Racket ensures that compile-time side effects are thrown away, and the run-time code starts over with fresh, untouched state. This guarantees consistent behavior, since it becomes impossible to distinguish at run-time whether a module was just compiled on the fly, or if it was pre-compiled long ago (possibly even on someone else’s computer).</p><p>This is the essence of the separate compilation guarantee. To summarize:</p><ul><li><p>Run-time and compile-time are distinct <em>phases</em> of execution, which cannot interact.</p></li><li><p>Modules can be required at multiple phases via <em>phase shifting</em>, but their state is kept separate. Each phase gets its own copy of the state.</p></li><li><p>Ensuring that the state is kept separate ensures predictable program behavior, no matter when the program is compiled.</p></li></ul><p>This summary is a simplification of phases in Racket. The full Racket module system does not have only two phases, since macros can also be <em>used</em> at compile-time to implement other macros, effectively creating a separate “compile-time” for the compile-time code. Each compile-time pass is isolated to its own phase, creating a finite but arbitrarily large number of distinct program phases (all but one of which occur at compile-time).</p><p>Furthermore, the separate compilation guarantee does not just isolate the state of each phase from the state of other phases but also isolates all compile-time state from the compile-time state of other modules. This ensures that compilation is still deterministic even if modules are compiled in a different <em>order</em>, or if several modules are sometimes compiled individually while other times compiled together all at once.</p><p>If you want to learn more, the full details of the module system are described at length in the <a href="https://docs.racket-lang.org/guide/phases.html">General Phase Levels</a> section of the Racket Guide, but the abridged summary I’ve described is enough for the purposes of this blog post. If the bulleted list above mostly made sense to you, you’re ready to move on.</p><h2 id="section:main-start">How we’re going to break it</h2><p>The separate compilation guarantee is a sturdy opponent, but it is not without weaknesses. Although no API in Racket, safe or unsafe, allows arbitrarily disabling phase separation, a couple features of Racket are already known to allow limited forms of cross-phase communication.</p><p>The most significant of these, and the one we’ll be using as our vector of attack, is the logger. Unlike many logging systems, which are exclusively string-oriented, Racket’s logging interface allows structured logging by associating an arbitrary Racket value with each and every log message. Since it is possible to set up listeners within Racket that receive log messages sent to a particular “topic,” the logger can be used as a communication channel to send values between different parts of a program.</p><p>The following program illustrates how this works. One thread creates a listener for all log messages on the topic <code>'send-me-a-value</code> using <code>make-log-receiver</code>, then uses <code>sync</code> to block until a value is received. Meanwhile, a second thread sends values through the logger using <code>log-message</code>. Together, this creates a makeshift buffered, asynchronous channel:</p><pre><code class="pygments"><span class="c1">;; log-comm.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">send-me-a-value</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span><span class="w"> </span><span class="c1">; wait forever</span></code></pre><pre><code>$ racket log-comm.rkt +'#(debug "" 1 send-me-a-value) +'#(debug "" 2 send-me-a-value) +'#(debug "" 3 send-me-a-value) +'#(debug "" 4 send-me-a-value) +^Cuser break +</code></pre><p>In this program, the value being sent through the logger is just a number, which isn’t very interesting. But the value really can be <em>any</em> value, even arbitrary closures or mutable data structures. It’s even possible to send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> through a logger, which can subsequently be used to communicate directly, without having to abuse the logger.</p><p>Generally, this feature of loggers isn’t very useful, since Racket has plenty of features for cross-thread communication. What’s special about the logger, however, is that it is global, and it is cross-phase.</p><p>The cross-phase nature of the logger makes some sense. If a Racket program creates a namespace (that is, a fresh environment for dynamic evaluation), then uses it to expand and compile a Racket module, the process of compilation might produce some log messages, and the calling thread might wish to receive them. It wouldn’t be a very useful logging system if log messages during compile-time were always lost. However, this convenience is a loophole in the phase separation system, since it allows values to flow—bidirectionally—between phases.</p><p>This concept forms the foundation of our exploit, but it alone is not a new technique, and I did not discover it. However, all existing uses I know of that use the logger for cross-phase communication require control of the parent namespace in which modules are being compiled, which means some code must exist “outside” the actual program. That technique does not work for ordinary programs run directly with <code>racket</code> or compiled directly with <code>raco make</code>, so to get there, we’ll need something more clever.</p><h3><a name="the-challenge"></a>The challenge</h3><p>Our goal, therefore, is to share state between phases <em>without</em> controlling the compilation namespace. More precisely, we want to be able to create an arbitrary module-level definition that is <em>cross-phase persistent</em>, which means it will be evaluated once and only once no matter how many times its enclosing module is re-instantiated (i.e. given a fresh, untouched state) at various phases. A phase-shifted <code>require</code> of the module that contains the definition should share state with an unshifted version of the module, breaking the separate compilation guarantee wide open.</p><p>To use the example from the previous section, we should be able to adjust <code>foods.rkt</code> very slightly…</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="c1">; share across phases</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="cm">#| ... |#</span></code></pre><p>…and the <code>delicious-foods</code> mutable state should magically become cross-phase persistent. When running <code>check-food.rkt</code> from source, we should see the side-effects persisted from the module’s compilation, while running from pre-compiled bytecode should give us the result with compile-time effects discarded.</p><p>We already know the logger is going to be part of our exploit, but implementing <code>define/cross-phase</code> on top of it is more subtle than it might seem. In our previous example that used <code>make-log-receiver</code>, we had well-defined sender and receiver threads, but who is the “sender” in our multi-phased world? And what exactly is the sender sending?</p><p>To answer those questions, allow me to outline the general idea of our approach:</p><ol><li><p>The first time our <code>foods.rkt</code> module is instantiated, at any phase, it evaluates the <code>(mutable-set)</code> expression to produce a new mutable set. It spawns a sender thread that sends this value via the logger to anyone who will listen, and that thread lingers in the background for the remaining duration of the program.</p></li><li><p>All subsequent instantiations of <code>foods.rkt</code> do <em>not</em> evaluate the <code>(mutable-set)</code> expression. Instead, they obtain the existing set by creating a log receiver and obtaining the value the sender thread is broadcasting. This ensures that a single value is shared across all instantiations of the module.</p></li></ol><p>This sounds deceptively simple, but the crux of the problem is how to determine whether <code>foods.rkt</code> has previously been instantiated or not. Since we can only communicate across phases via the logger, we cannot use any shared state to directly record the first time the module is instantiated. We can listen to a log receiver and wait to see if we get a response, but this introduces a race condition: how long do we wait until giving up and deciding we’re the first instantiation? Worse, what if two threads instantiate the module at the same time, and both threads end up spawning a new sender thread, duplicating the state?</p><p>The true challenge, therefore, is to develop a protocol by which we can be <em>certain</em> we are the first instantiation of a module, without relying on any unspecified behavior, and without introducing any race conditions. This is possible, but it isn’t obvious, and it requires combining loggers with some extra tools available to the Racket programmer.</p><h3><a name="the-key-idea"></a>The key idea</h3><p>It’s finally time to tackle the key idea at the heart of our exploit: garbage collection. In Racket, garbage collection is an observable effect, since Racket allows attaching finalizers to values via <a href="https://docs.racket-lang.org/reference/willexecutor.html">wills and executors</a>. Since a single heap is necessarily shared by the entire VM, behavior happening on other threads (even in other phases) can be indirectly observed by creating a unique value—a “canary”—then sending it to another thread, and waiting to see if it will be garbage collected or not (that is, whether or not the canary “dies”).</p><p>Remember that logs and log receivers are effectively buffered, multicast, asynchronous FIFO channels. Since they are buffered, if any thread is already listening to a logger topic when a value is sent, it cannot possibly be garbage collected until that thread either reads it and discards it or the receiver itself is garbage collected. It’s possible to use this mechanism to observe whether or not another thread is already listening on a topic, as the following program demonstrates:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">executor</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><pre><code>$ racket check-receivers.rkt +no receivers for 'bar +receiver exists for 'foo +</code></pre><p>However, this program has some problems. For one, it needs to call <code>collect-garbage</code> several times to be certain that the canary will be collected if there are no listeners, which can take a second or two, and it also assumes that three calls to <code>collect-garbage</code> will be enough to collect the canary, though there is no guarantee that will be true.</p><p>A bulletproof solution should be both reasonably performant and guaranteed to work. To get there, we have to combine this idea with something more. Here’s the trick: instead of sending the canary alone, send a <a href="https://docs.racket-lang.org/guide/concurrency.html#%28part._.Channels%29">channel</a> alongside it. Synchronize on both the canary’s executor <em>and</em> the channel so that the thread will unblock if either the canary is collected <em>or</em> the channel is received and sent a value using <code>channel-put</code>. Have the receiver listen for the channel on a separate thread, and when it receives it, send a value back to unblock the waiting thread as quickly as possible, without needing to rely on a timeout or a particular number of calls to <code>collect-garbage</code>.</p><p>Using that idea, we can revise the program:</p><pre><code class="pygments"><span class="c1">;; check-receivers.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; limit scope of `canary` so we don’t retain a reference</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="nb">void</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="c1">; send the channel + the canary</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">canary</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span><span class="w"> </span><span class="c1">; yield to try to let the receiver thread work</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">received</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">)))</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">received</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span><span class="w"> </span><span class="c1">; collect garbage and try again</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"no receivers for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">printf</span><span class="w"> </span><span class="s2">"receiver exists for ~v</span><span class="se">\n</span><span class="s2">"</span><span class="w"> </span><span class="n">topic</span><span class="p">)))</span> + +<span class="c1">; add a receiver on topic &#39;foo</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">recv</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))</span> +<span class="p">(</span><span class="nb">void</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">recv</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="k">_</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t1</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">foo</span><span class="p">))))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">t2</span><span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="n">check-receivers</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">bar</span><span class="p">))))</span> + +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t1</span><span class="p">)</span> +<span class="p">(</span><span class="nb">thread-wait</span><span class="w"> </span><span class="n">t2</span><span class="p">)</span></code></pre><p>Now the program completes almost instantly. For this simple program, the explicit <code>(sleep)</code> call is effective enough at yielding that, on my machine, <code>(check-receivers 'foo)</code> returns without ever calling <code>collect-garbage</code>, and <code>(check-receivers 'bar)</code> returns after performing a single minor collection.</p><p>This is extremely close to a bulletproof solution, but there are two remaining subtle issues:</p><ol><li><p>There is technically a race condition between the <code>(sync recv)</code> in the receiver thread and the subsequent <code>channel-put</code>, since it’s possible for the canary to be received, discarded, and garbage collected before reaching the call to <code>channel-put</code>, which the sending thread would incorrectly interpret as indicating the topic has no receivers.</p><p>To fix that, the receiver thread can send the canary itself back through the channel, which fundamentally has to work, since the value cannot be collected until it has been received by the sending thread, at which point the <code>sync</code> has already chosen the channel.</p></li><li><p>It is possible for the receiver thread to receive the log message and call <code>channel-put</code>, but for the sending thread to somehow die in the meantime (which cannot be protected against in general in Racket, since <code>thread-kill</code> immediately and forcibly terminates a thread). If this were to happen, the sending thread would never obtain the value from the channel, blocking the receiving thread indefinitely.</p><p>A solution is to spawn a new thread for each <code>channel-put</code> instead of calling it directly from the receiving thread. Conveniently, this both ensures the receiving thread never gets stuck and avoids resource leaks, since the Racket runtime is smart enough to GC a thread blocked on a channel that has no other references and therefore can never be unblocked.</p></li></ol><p>With those fixes in place, the program is, to the best of my knowledge, bulletproof. It will always correctly determine whether or not a logger has a listener, with no race conditions or reliance upon unspecified behavior of the Racket runtime. It does, however, make a couple of assumptions.</p><p>First, it assumes that the value of <code>(current-logger)</code> is shared between the threads. It is true that <code>(current-logger)</code> can be changed, and sometimes is, but it’s usually done via <code>parameterize</code>, not mutation of the parameter directly. Therefore, this can largely be mitigated by storing the value of <code>(current-logger)</code> at module instantiation time.</p><p>Second, it assumes that no other receivers are listening on the same topic. Technically, even using a unique, uninterned key for the topic is insufficient to ensure that no receivers are listening to it, since a receiver can choose to listen to all topics. However, in practice, it is highly unlikely that any receiver would willfully choose to listen to all topics at the <code>'debug</code> level, since the receiver would be inundated with enormous amounts of useless information. Even if such a receiver were to be created, it is highly likely that it would dequeue the messages as quickly as possible and discard the accompanying payload, since doing otherwise would cause all messages to be retained in memory, leading to a significant memory leak.</p><p>Both these problems can be mitigated by using a logger other than the root logger, which is easy in this example. However, for the purpose of subverting the separate compilation guarantee, we would have no way to share the logger object itself across phases, defeating the whole purpose, so we are forced to use the root logger and hope the above two assumptions remain true (but they usually do).</p><h3><a name="preparing-the-exploit"></a>Preparing the exploit</h3><p>If you’ve made it here, congratulations! The most difficult part of this blog post is over. All that’s left is the fun part: performing the exploit.</p><p>The bulk of our implementation is a slightly adapted version of <code>check-receivers</code>:</p><pre><code class="pygments"><span class="c1">;; define-cross-phase.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="p">(</span><span class="nb">current-logger</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="k">thunk</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">receiver</span><span class="w"> </span><span class="p">(</span><span class="nb">make-log-receiver</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">make-channel</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="p">(</span><span class="nb">make-will-executor</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">canary</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">will-register</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">collected</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">log-message</span><span class="w"> </span><span class="n">root-logger</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">debug</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="s2">""</span> +<span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="k">==</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="nb">eq?</span><span class="p">))</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">void</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)])))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">execute-evt</span><span class="w"> </span><span class="p">(</span><span class="nb">wrap-evt</span><span class="w"> </span><span class="n">executor</span><span class="w"> </span><span class="nb">will-execute</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">n</span><span class="w"> </span><span class="mi">0</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">sleep</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="nb">sync/timeout</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="n">execute-evt</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="nb">collect-garbage</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">&lt;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">minor</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">major</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="n">n</span><span class="p">))))))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">result</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">value</span><span class="p">)</span> +<span class="w"> </span><span class="n">value</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="o">&#39;</span><span class="ss">collected</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="p">(</span><span class="k">thunk</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="p">(</span><span class="nb">sync</span><span class="w"> </span><span class="n">receiver</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">vector</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="nb">vector</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">chan</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">thread</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="nb">channel-put</span><span class="w"> </span><span class="n">chan</span><span class="w"> </span><span class="p">(</span><span class="nb">vector-immutable</span><span class="w"> </span><span class="n">canary</span><span class="w"> </span><span class="n">value</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="p">)]))))</span> +<span class="w"> </span><span class="n">value</span><span class="p">]))</span></code></pre><p>There are a few minor differences, which I’ll list:</p><ol><li><p>The most obvious difference is that <code>make-cross-phase</code> does the work of both checking if a receiver exists—which I’ll call the <em>manager thread</em>—and spawning it if it doesn’t. If it does end up spawning a manager thread, it evaluates the given thunk to produce a value, which becomes the cross-phase value that will be sent through the channel alongside the canary.</p></li><li><p>Once the manager thread is created, subsequent calls to <code>make-cross-phase</code> will receive the value through the channel and return it instead of re-invoking <code>thunk</code>. This is what ensures the right-hand side of each use of <code>define/cross-phase</code> is only ever evaluated once.</p></li><li><p>Since <code>make-cross-phase</code> needs to create a log receiver if no manager thread exists, it does so immediately, before sending the canary through the logger. This avoids a race condition between multiple threads that are simultaneously competing to become the manager thread, where both threads could send a canary through the logger before either was listening, both canaries would get GC’d, and both threads would spawn a new manager.<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><p>Creating the receiver before sending the canary avoids this problem, but the thread now needs to receive its own canary and discard it before synchronizing on the channel and executor, since otherwise it will retain a reference to the canary. It’s possible that in between creating the receiver and sending the canary, another thread also sent a canary, so it needs to drop any log messages it finds that don’t include its own canary.</p><p>This ends up working out perfectly, since every thread drops all the messages received before the one containing its own canary, but retains all subsequent values. This means that only one thread can ever “win” and become the manager, since the first thread to send a canary is guaranteed to retain all subsequent canaries, yet also guaranteed its canary will be GC’d. Other threads racing to become the manager will remain blocked until the manager thread is created, since its canaries will be retained by the manager-to-be until it dequeues them.</p><p>(This is the most subtle part of the process to get right, but conveniently, it mostly just works out without very much code. If you didn’t understand any of the above three paragraphs, it isn’t a big deal.)</p></li></ol><p>The final piece to this puzzle is to define the <code>define/cross-phase</code> macro that wraps <code>make-cross-phase</code>. The macro is actually slightly more involved than just generating a call to <code>make-cross-phase</code> directly, since we’d like to use an uninterned symbol for the topic instead of an interned one, just to ensure it is unique. Ordinarily, this might seem impossible, since an uninterned symbol is fundamentally a unique value that needs to be communicated across phases, and the whole problem we are solving is creating a communication channel that spans phases. However, Racket actually provides some built-in support for sharing uninterned symbols across phases (plus some other kinds of values, but they must always be immutable). To do this, we need to generate a <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._cross-phase._persistent-modules%29">cross-phase persistent submodule</a> that exports an uninterned symbol, then pass that symbol as the topic to <code>make-cross-phase</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">define/cross-phase</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">cross-phase-topic-key</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">topic-mod-name</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">#%kernel</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%declare</span><span class="w"> </span><span class="kd">#:cross-phase-persistent</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%provide</span><span class="w"> </span><span class="n">topic</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">topic</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="w"> </span><span class="s2">"cross-phase"</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">topic-mod-name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="n">make-cross-phase</span><span class="w"> </span><span class="n">topic</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">e</span><span class="p">)))))</span></code></pre><p>And that’s really it. We’re done.</p><h3><a name="executing-the-exploit"></a>Executing the exploit</h3><p>With our implementation of <code>define/cross-phase</code> in hand, all that’s left to do is run our original <code>check-foods.rkt</code> program and see what happens:</p><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +set-add!:<span class="w"> </span>contract<span class="w"> </span>violation: +expected:<span class="w"> </span>set? +given:<span class="w"> </span><span class="o">(</span>mutable-set<span class="w"> </span><span class="s2">"fried chicken"</span><span class="w"> </span><span class="s2">"roasted chicken"</span><span class="w"> </span><span class="s2">"roasted potato"</span><span class="w"> </span><span class="s2">"fried potato"</span><span class="o">)</span> +argument<span class="w"> </span>position:<span class="w"> </span>1st +other<span class="w"> </span>arguments...: +<span class="w"> </span>x:<span class="w"> </span><span class="s2">"pineapple"</span></code></pre><p>Well, I don’t know what you expected. Play stupid games, win stupid prizes.</p><p>This error actually makes sense, but it belies one reason (of many) why this whole endeavor is probably a bad idea. Although we’ve managed to make our mutable set cross-phase persistent, our references to set operations like <code>set-add!</code> and <code>set-member?</code> are not, and every time <code>racket/set</code> is instantiated in a fresh phase, it creates an entirely new instance of the <code>set</code> structure type. This means that even though we have a bona fide mutable set, it isn’t actually the type of set that this phase’s <code>set-add!</code> understands!</p><p>Of course, this isn’t a problem that some liberal application of <code>define/cross-phase</code> can’t solve:</p><pre><code class="pygments"><span class="c1">;; foods.rkt</span> +<span class="kn">#lang </span><span class="nn">racket</span> +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="s2">"define-cross-phase.rkt"</span><span class="p">)</span> +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">add-delicious-food!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-member?</span><span class="w"> </span><span class="nb">set-member?</span><span class="p">)</span> +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">cross:set-add!</span><span class="w"> </span><span class="nb">set-add!</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/cross-phase</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="p">(</span><span class="nb">mutable-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">delicious-food?</span><span class="w"> </span><span class="n">food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-member?</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">food</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-delicious-food!</span><span class="w"> </span><span class="n">new-food</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">cross:set-add!</span><span class="w"> </span><span class="n">delicious-foods</span><span class="w"> </span><span class="n">new-food</span><span class="p">))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>a<span class="w"> </span>delicious<span class="w"> </span>food. +$<span class="w"> </span>raco<span class="w"> </span>make<span class="w"> </span>check-food.rkt +$<span class="w"> </span>racket<span class="w"> </span>check-food.rkt<span class="w"> </span><span class="s1">&#39;fried chicken&#39;</span> +fried<span class="w"> </span>chicken<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span>delicious.</code></pre><p>And thus we find that another so-called “guarantee” isn’t.</p><h2><a name="reflection"></a>Reflection</h2><p>Now comes the time in the blog post when I have to step back and think about what I’ve done. Have mercy.</p><p>Everything in this blog post is a terrible idea. No, you should not use loggers for anything that isn’t logging, you shouldn’t use wills and executors for critical control flow, and obviously you should absolutely not intentionally break one of the most helpful guarantees the Racket module system affords you.</p><p>But I thought it was fun to do all that, anyway.</p><p>The meaningful takeaways from this blog post aren’t that the separate compilation guarantee can be broken, nor that any of the particular techniques I used hold, but that</p><ol><li><p>ensuring non-trivial guarantees is really hard,</p></li><li><p>despite that, the separate compilation guarantee is really, really hard to break,</p></li><li><p>the separate compilation guarantee is good, and you should appreciate the luxury it affords you while writing Racket macros,</p></li><li><p>avoiding races in a concurrent environment can be extremely subtle,</p></li><li><p>and Racket is totally <em>awesome</em> for giving me this much rope to hang myself with.</p></li></ol><p>If you want to hang yourself with Racket, too, <a href="https://gist.github.com/lexi-lambda/f173a84fc9727977bcea657b3bb0cd4f">runnable code from this blog post is available here</a>.</p><ol class="footnotes"><li id="footnote-1"><p>This isn’t <em>strictly</em> true, since Racket provides sandboxing mechanisms that can compile and execute untrusted code without file system or network access, but this is not the default compilation mode. Usually, it doesn’t matter nearly as much as it might sound: most of the time, if you’re compiling untrusted code, you’re also going to run it, and running untrusted code can do all those things, anyway. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>This is actually a <em>terrible</em> use case for a macro, since an ordinary function would do just fine, but I’m simplifying a little to keep the example small. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Racket actually provides this functionality directly via the <code>log-level?</code> procedure. However, since <code>log-level?</code> provides no way to determine how <em>many</em> receivers are listening to a topic, using it to guard against creating a receiver is vulnerable to a race condition that the garbage collection-based approach can avoid, as is discussed later. Furthermore, the GC technique is more likely to be resilient to nosy log receivers listening on all topics at the <code>'debug</code> level, since they will almost certainly dequeue and discard the value quickly (as otherwise they would leak large quantities of memory). <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>This race is the one that makes using <code>log-level?</code> untenable, since the receiver needs to be created before the topic is checked for listeners to avoid the race, which can’t be done with <code>log-level?</code> (since it would always return <code>#t</code>). <a href="#footnote-ref-4-1">↩</a></p></li></ol></article>Macroexpand anywhere with local-apply-transformer!https://lexi-lambda.github.io/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/https://lexi-lambda.github.io/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/06 Oct 2018<article><p>Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only <em>certain positions</em> in Racket code are subject to macroexpansion.</p><p>To illustrate, consider a macro that provides a Clojure-style <code>let</code> form:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This can be used anywhere an expression is expected, and it does as one would expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">clj-let</span><span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>However, a novice macro programmer might realize that <code>clj-let</code> really only modifies the syntax of <em>binding pairs</em> for a <code>let</code> form. Therefore, could one define a macro that only adjusts the binding pairs of some existing <code>let</code> form instead of expanding to an entire <code>let</code>? That is, could one write the above example like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span> +<span class="mi">3</span></code></pre><p>The answer is <em>no</em>: the binding pairs of a <code>let</code> form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion <em>anywhere</em> in a Racket program.</p><h2><a name="why-only-some-positions-are-subject-to-macroexpansion"></a>Why only some positions are subject to macroexpansion</h2><p>To understand <em>why</em> the macroexpander refuses to touch certain positions in a program, we must first understand how the macro system operates. In Racket, a macro is defined as a compile-time function associated with a particular binding, and macros are given complete control over the syntax trees they are surrounded with. If we define a macro <em><code>mac</code></em>, then we write the expression <code>(<em>mac</em> <em>form</em>)</code>, <em><code>form</code></em> is provided as-is to <em><code>mac</code></em> as a syntax object. Its structure can be anything at all, since <em><code>mac</code></em> can be an arbitrary Racket function, and that function can use <em><code>form</code></em> however it pleases.</p><p>To give a concrete illustration, consider a macro that binds some identifiers to symbols in a local scope:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">x</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="ss">hello</span><span class="w"> </span><span class="ss">goodbye</span><span class="p">)</span></code></pre><p>It isn’t the most exciting macro in the world, but it illustrates a key point: the first subform to <code>let-symbols</code> is a list of identifiers that are eventually put in <em>binding</em> position. This means that <code>hello</code> and <code>goodbye</code> are bindings, not uses, and such bindings shadow any existing bindings that might have been in scope:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">foo</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="p">)</span> +<span class="w"> </span><span class="n">foo</span><span class="p">))</span> +<span class="o">&#39;</span><span class="ss">foo</span></code></pre><p>This might not seem very interesting, but it’s critical to understand, since it means that the expander <em>can’t know</em> which sub-pieces of a use of <code>let-symbols</code> will eventually be expressions themselves until it expands the macro and discovers it produces a <code>let</code> form, so it can’t know where it’s safe to perform macroexpansion. To make this more explicit, imagine we define a macro under some name, then try and use that name with our <code>let-symbols</code> macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">x:id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">x:id</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">let-symbols</span><span class="w"> </span><span class="p">(</span><span class="n">hello</span><span class="w"> </span><span class="n">goodbye</span><span class="p">)</span> +<span class="w"> </span><span class="n">hello</span><span class="p">)</span></code></pre><p>What should the above program do? If we treat the first use of <code>hello</code> in the <code>let-symbols</code> form as a macro application, then <code>(hello goodbye)</code> should be transformed into <code>(goodbye)</code>, and the use of <code>hello</code> in the body should be a syntax error. But if the first use of <code>hello</code> was instead intended to be a binder, then it should shadow the <code>hello</code> definition above, and the output of the program should be <code>'hello</code>.</p><p>To avoid the chaos that would ensue if defining a macro could completely break local reasoning about other macros, Racket chooses the second option, and the program produces <code>'hello</code>. The macroexpander has no way of knowing <em>how</em> each macro will inspect its constituent pieces, so it avoids touching anything until the macro expands. After it discovers the <code>let</code> form in the expansion of <code>let-symbols</code>, it can safely determine that the body expressions are, indeed, expressions, and it can recursively expand any macros they contain. To put things another way, a macro’s sub-forms are never expanded before the macro itself is expanded, only after.</p><h2><a name="forcing-sub-form-expansion"></a>Forcing sub-form expansion</h2><p>The above section explains why the expander must operate as it does, but it’s a little bit unsatisfying. What if we write a macro where we <em>want</em> certain sub-forms to be expanded before they are passed to us? Fortunately, the Racket macro system provides an API to handle this use case, too.</p><p>It is true that the Racket macro system never <em>automatically</em> expands sub-forms before outer forms are expanded, but macro transformers can explicitly op-in to recursive expansion via the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a> function. This function effectively yields control back to the expander to expand some arbitrary piece of syntax as an expression, and when it returns, the macro transformer can inspect the expanded expression however it wishes. In theory, this can be used to implement extensible macros that allow macroexpansion in locations other than expression position.</p><p>To give an example of such a macro, consider the Racket <code>match</code> form, which implements an expressive pattern-matcher as a macro. One of the most interesting qualities of Racket’s <code>match</code> macro is that its pattern language is user-extensible, essentially allowing pattern-level macros. For example, a user might find they frequently match against natural numbers, and they wish to be able to write <code>(nat n)</code> as a shorthand for <code>(? exact-nonnegative-integer? n)</code>. Fortunately, this is easy using <code>define-match-expander</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-match-expander</span><span class="w"> </span><span class="n">nat</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">pat</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">?</span><span class="w"> </span><span class="nb">exact-nonnegative-integer?</span><span class="w"> </span><span class="n">pat</span><span class="p">)]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="mi">-5</span><span class="w"> </span><span class="mi">-2</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="mi">-7</span><span class="p">)</span> +<span class="w"> </span><span class="p">[(</span><span class="nb">list</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">nat</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">n</span><span class="p">])</span> +<span class="mi">4</span></code></pre><p>Clearly, <code>match</code> is somehow expanding the <code>nat</code> match expander as a part of its expansion. Is it using <code>local-expand</code>?</p><p>Well, no. While <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">a previous blog post of mine</a> has illustrated that it is possible to do such a thing with <code>local-expand</code> via some clever trickery, <code>local-expand</code> is really designed to expand <em>expressions</em>. This is a problem, since <code>(nat n)</code> is not an expression, it’s a pattern: it will expand into <code>(? exact-nonnegative-integer? n)</code>, which will lead to a syntax error, since <code>?</code> is not bound in the world of expressions. Instead, for a long while, <code>match</code> and forms like it have emulated how the expander performs macroexpansion in ad-hoc ways. Fortunately, as of Racket v7.0, the new <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Fapply-transformer..rkt%29._local-apply-transformer%29%29"><code>local-apply-transformer</code></a> API provides a way to invoke recursive macroexpansion in a consistent way, and it doesn’t assume that what’s being expanded is an expression.</p><h3><a name="a-closer-look-at-local-apply-transformer"></a>A closer look at <code>local-apply-transformer</code></h3><p>If <code>local-apply-transformer</code> is the answer, what does it actually do? Well, <code>local-apply-transformer</code> allows explicitly invoking a transformer function on some piece of syntax and retrieving the result. In other words, <code>local-apply-transformer</code> allows expanding an arbitrary macro, but since it doesn’t make any assumptions about what the output will be, it only expands it <em>once</em>: just a single step of macro transformation.</p><p>To illustrate, we can write a macro that uses <code>local-apply-transformer</code> to invoke a transformer function and preserve the result using <code>quote-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/apply-transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define-for-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="n">flip</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>When we use <code>mac</code>, our <code>flip</code> function will be applied, as a macro, to the syntax object we provide:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">((</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="nb">&gt;</span></code></pre><p>Alright, so this works, but it raises some questions. Why is <code>flip</code> defined as a function at phase 1 (using <code>define-for-syntax</code>) instead of as a macro (using <code>define-syntax</code>)? What’s the deal with the <code>'expression</code> argument to <code>local-apply-transformer</code> given that <code>local-apply-transformer</code> is supposedly decoupled from expression expansion? And finally, how is this any different from just calling our <code>flip</code> function on the syntax object directly by writing <code>(flip #'(([x 1]) let x))</code>?</p><p>Let’s start with the first of those questions: why is <code>flip</code> defined as a function rather than as a macro? Well, <code>local-apply-transformer</code> is a fairly low-level operation: remember, it doesn’t assume <em>anything</em> about the argument it’s given! Therefore, it doesn’t take an expression containing a macro and expand it based on its structure, it needs to be explicitly provided the macro transformer function to apply. In practice, this might not seem very useful, since presumably we want to write our macros as macros, not as phase 1 functions. Fortunately, it’s possible to look up the function associated with a macro binding using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, so if we use that, we can define <code>flip</code> using <code>define-syntax</code> as usual:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>Now for the next question: what is the meaning of the <code>'expression</code> argument? This one is more of a historical artifact than anything else: when the expander applies a macro transformer, it does it in a “context”, which is accessible using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-context%29%29"><code>syntax-local-context</code></a> function. This context can be one of a predefined enumeration of cases, including <code>'expression</code>, <code>'top-level</code>, <code>'module</code>, <code>'module-begin</code>, or a list representing a definition context. Whether or not any of those actually apply to our use case, we still have to pick one, but aside from how they affect the value returned by <code>syntax-local-context</code> (which some macros inspect), the value we choose is largely irrelevant. Using <code>'expression</code> will do, even if it’s a bit of a lie.</p><p>Finally, how does any of this differ from just applying the function we get directly? Well, the critical answer is all about <em>hygiene</em>. Racket’s macro system is hygienic, which, among other things, ensures bindings defined with the same name in different places do not unintentionally conflict. Racket’s hygiene mechanism is implemented in the macroexpander, when macro transformers are applied. If we just applied the <code>flip</code> transformer procedure to a syntax object directly, we would circumvent this hygiene mechanism, potentially causing all sorts of problems. By using <code>local-apply-transformer</code>, we ensure hygiene is preserved.</p><p>There is one small problem left with our program, however. Can you spot it? The key is to consider what would happen if we used <code>flip</code> as an ordinary macro, without using <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="n">let:</span><span class="w"> </span><span class="n">bad</span><span class="w"> </span><span class="k">syntax</span> +<span class="w"> </span><span class="n">in:</span><span class="w"> </span><span class="k">let</span></code></pre><p>What happened? Well, remember that when a macro in Racket is used, it receives the whole use site as a syntax object: in this case, <code>#'(flip (([x 1]) let x))</code>. This means that <code>flip</code> ought to be written to parse its argument slightly differently:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">flip</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]))</span></code></pre><p>Indeed, now that we’ve properly restructured the macro, we can easily switch to using the convenient <code>define-simple-macro</code> shorthand:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>This means we also need to update our definition of <code>mac</code> to provide the full syntax object the expander would:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">mac</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">flip</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">flip</span><span class="w"> </span><span class="p">(([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="k">let</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">quote-syntax</span><span class="w"> </span><span class="n">result</span><span class="p">))</span></code></pre><p>This might seem redundant, but remember, <code>local-apply-transformer</code> is very low-level! While the convention that <code>(<em>mac</em> . _)</code> is the syntax for a macro transformation might seem obvious, <code>local-apply-transformer</code> makes no assumptions. It just does what we tell it to do.</p><h3><a name="applying-local-apply-transformer"></a>Applying <code>local-apply-transformer</code></h3><p>So what does <code>local-apply-transformer</code> have to do with the problem at the beginning of this blog post? Well, as it happens, we can use <code>local-apply-transformer</code> to implement a macro that allows expansion <em>anywhere</em> using some simple tricks. While it’s true that we cannot magically divine which locations ought to be expanded, what we <em>can</em> do is explicitly annotate which places to expand.</p><p>To do this, we will implement a macro, <code>expand-inside</code>, that looks for subforms annotated with a special <code>$expand</code> identifier and performs macro transformation on those locations before proceeding with ordinary macroexpansion. Using the <code>clj-binding-pairs</code> example from the beginning of this blog post, our solution to that problem will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Put another way, <code>expand-inside</code> will force eager expansion on any subform surrounded with an <code>$expand</code> annotation.</p><p>We’ll start by defining the <code>$expand</code> binding itself. This binding won’t mean anything at all outside of <code>expand-inside</code>, but we’d like it to be a unique binding so that users can rename it (using, <code>rename-in</code>, for example) if they wish. To do this, we’ll use the usual trick of defining it as a macro that always produces an error if it’s ever used:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"illegal outside an ‘expand-inside’ form"</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span></code></pre><p>Next, we’ll implement a syntax class that will form the bulk of our implementation of <code>expand-inside</code>. Since we need to find uses of <code>$expand</code> that might be deeply-nested inside the syntax object provided to <code>expand-inside</code>, we need to recursively look through the syntax object, find any instances of <code>$expand</code>, and put it all back together once we’re done. This can be done relatively cleanly using a recursive syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">do-expand-inside</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="n">$expand</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">$expand</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:do-expand-inside</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">a:do-expand-inside</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">b:do-expand-inside</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">reassembled</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">a.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">reassembled</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)</span> +<span class="w"> </span><span class="n">reassembled</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>There are some tricky details to get right in the reassembly of pairs, since syntax lists are actually composed of ordinary pairs rather than syntax pairs, but ultimately, the code for walking a syntax object is small. The key case of this syntax class is the call to <code>do-$expand</code> in the first clause, which we have not yet defined. This function will actually handle performing the expansion by invoking <code>local-apply-transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">do-$expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">trans</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)}})</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">trans</span><span class="w"> </span><span class="p">(</span><span class="n">static</span><span class="w"> </span><span class="p">(</span><span class="nb">disjoin</span><span class="w"> </span><span class="nb">procedure?</span><span class="w"> </span><span class="nb">set!-transformer?</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"syntax transformer"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">local-apply-transformer</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">trans.value</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span> +<span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">)])))</span></code></pre><p>This uses the handy <code>static</code> syntax class that comes with <code>syntax/parse</code>, which implicitly handles the call to <code>syntax-local-value</code> and produces a nice error message if the value returned does not match a predicate. All we have to do is apply the transformer value bound to the <code>trans.value</code> attribute using <code>local-apply-transformer</code>, and now the <code>expand-macro</code> can be written in just a couple lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">expand-inside</span> +<span class="w"> </span><span class="kd">#:track-literals</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:do-expand-inside</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form.expansion</span><span class="p">])</span></code></pre><p>(Using the <code>#:track-literals</code> option, also new in Racket v7.0, ensures that Check Syntax will be able to recognize the uses of <code>$expand</code> that disappear from after <code>expand-inside</code> is expanded.)</p><p>Putting everything together, our example from above really works:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span><span class="w"> </span><span class="p">[{</span><span class="n">~seq</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">e:expr</span><span class="p">}</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="n">e</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-inside</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">(</span><span class="n">$expand</span> +<span class="w"> </span><span class="p">(</span><span class="n">clj-binding-pairs</span> +<span class="w"> </span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span> +<span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">2</span><span class="p">]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>That’s it. All told, the entire implementation is only about 30 lines of code. For a full, compilable, working example, see <a href="https://gist.github.com/lexi-lambda/65d69043023b519694f50dfca2dc7d33">this gist</a>.</p><ol class="footnotes"></ol></article>Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitionshttps://lexi-lambda.github.io/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/https://lexi-lambda.github.io/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/13 Sep 2018<article><p>In my <a href="/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/">previous blog post</a>, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.</p><h2><a name="what-are-internal-definitions"></a>What are internal definitions?</h2><p>This blog post is going to be largely focused on how to properly implement a form that handles the expansion of <em>internal definitions</em> in Racket. This is a tricky topic to get right, but before we can discuss internal definitions, we have to establish what definitions themselves are and how they relate to other binding forms.</p><p>In a traditional Lisp, there are two kinds of bindings: top-level bindings and local bindings. In Scheme and its descendants, this distinction is characterized by two different binding forms, <code>define</code> and <code>let</code>. To a first approximation, <code>define</code> is used for defining top-level, global bindings, and it resembles variable definitions in many mainstream languages in the sense that definitions using <code>define</code> are not really expressions. They don’t produce a value, they define a new binding. Definitions written with <code>define</code> look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="s2">"hello"</span><span class="p">)</span></code></pre><p>Each definition is made up of two parts: the <em>binding identifier</em>, in this case <code>x</code> and <code>y</code>, and the <em>right hand side</em>, or RHS for short. Each RHS is a single expression that will be evaluated and used as the value for the introduced binding.</p><p>In Scheme and Racket, <code>define</code> also supports a shorthand form for defining functions in a natural syntax without the explicit need to write <code>lambda</code>, which looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">double</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span></code></pre><p>However, this is just syntactic sugar. The above form is really just a macro for the following equivalent, expanded version:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">double</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)))</span></code></pre><p>Since we only care about fully-expanded programs, we’ll focus exclusively on the expanded version of <code>define</code> in this blog post, since if we handle that, we’ll also handle the function shorthand’s expansion.</p><p>In contrast to <code>define</code>, there is also <code>let</code>, which has a rather different shape. A <code>let</code> form <em>is</em> an expression, and it creates local bindings in a delimited scope:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>The binding clauses of a <code>let</code> expression are known as the <em>binding pairs</em>, and the sequence of expressions afterwards are known as the <em>body</em> of the <code>let</code>. Each binding pair consists of a binding identifier and a RHS, just like a top-level definition created with <code>define</code>, but while <code>define</code> is a standalone form, the binding pairs cannot meaningfully exist outside of a <code>let</code>—they are recognized as part of the grammar of the <code>let</code> form itself.</p><p>Like other Lisps, Racket distinguishes between top-level—or, more precisely, <em>module-level</em>—bindings and local bindings. A module-level binding can be exported using <code>provide</code>, which will allow other modules to access the binding by importing the module with <code>require</code>. Such definitions are treated specially by the macroexpander, compiler, and runtime system alike. There is a pervasive, meaningful difference between module-level definitions and local definitions besides simply scope.</p><p>I am making an effort to make this as clear as possible before discussing internal definitions because without it, the following point can be rather confusing: internal definitions are written using <code>define</code>, but they are local bindings, <em>not</em> module-level ones! In Racket, <code>define</code> is allowed to appear in the body of virtually all block forms like <code>let</code>, so the following is a legal program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">))</span></code></pre><p>This program is equivalent to the one expressed using <code>let</code>. In fact, when the Racket macroexpander expands these local uses of <code>define</code>, it actually translates them into uses of <code>letrec</code>. After expanding the above expression, it would look closer to the following:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)))</span></code></pre><p>In this sense, <code>define</code> is a form with a double life in Racket. When used at the module level, it creates module-level definitions, which remain in a fully-expanded program and can be imported by other modules. When used inside local blocks, it creates internal definitions, which do not remain in fully expanded programs, since they are translated into recursive local binding forms.</p><p>In this blog post, we will ignore module-level definitions. Like in the previous blog post, we will focus exclusively on expanding expressions, not whole modules. However, we will extend our language to allow internal definitions inside local binding forms, and we will translate them into <code>letrec</code> forms in the same way as the Racket macroexpander.</p><h2><a name="revisiting-and-generalizing-the-expression-expander"></a>Revisiting and generalizing the expression expander</h2><p>In the previous blog post, our expander expanded types, which were essentially expressions from the perspective of the Racket macroexpander. We wrote a syntax class that handled the parsing of a restricted type grammar that disallowed most Racket-level expression forms, like <code>begin</code>, <code>if</code>, <code>#%plain-lambda</code>, and <code>quote</code>. After all, Hackett is not dependently-typed, and it disallows explicit type abstraction to preserve type inference, so it would be a very bad thing if we allowed <code>if</code> or explicit lambda abstraction to appear in our types. For this blog post, however, we will restructure the type expander to handle the full grammar of expressions permitted by Racket.</p><p>While the syntax class approach used in the previous blog post was cute, this blog post will use ordinary functions defined at phase 1 instead of syntax classes. In practice, this provides superior error reporting, since it reports syntax errors in terms of the form that went wrong, not the form prior to expansion. Since we can still use <code>syntax-parse</code> to parse the arguments to these functions, we don’t lose any expressive power in the expression of our pattern language.</p><p>To start, we’ll extract the call to <code>local-expand</code> into its own function. This corresponds to the <code>type</code> syntax class from the previous blog post, but we’ll use phase 1 parameters to avoid threading so many explicit function arguments around:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="no">#f</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-context</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-stop-list</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">))))</span></code></pre><p>Due to the way <code>local-expand</code> implicitly extends the stop list, as discussed in the previous blog post, we can initialize the stop list to a list containing just <code>define-values</code> and <code>define-syntaxes</code>, and the other forms we care about will be included automatically.</p><p>Next, we’ll use this function to implement a <code>expand-expression</code> function, which will emulate the way the expander expands a single expression, as the name implies. We’ll ignore any custom core forms for now, so we’ll just focus exclusively on the Racket core forms.</p><p>A few of Racket’s core forms are not actually subject to any expansion at all, and they expand to themselves. These forms are <code>quote</code>, <code>quote-syntax</code>, and <code>#%variable-reference</code>. Additionally, <code>#%top</code> is not something useful to handle ourselves, since it involves no recursive expansion, so we’ll treat it as if it expands to itself as well and allow the expander to raise any unbound identifier errors it produces. Here’s what the <code>expand-expression</code> function looks like when exclusively handling these things:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Another set of Racket core forms are simple expressions which contain subforms, all of which are themselves expressions. These forms include things like <code>#%expression</code>, <code>begin</code>, and <code>if</code>, and they can be expanded recursively. We’ll add another clause to handle these, which can be written with a straightforward recursive call to <code>expand-expression</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Another easy form to handle is <code>set!</code>, since it also requires simple recursive expansion, but it can’t be handled in the same way as the above forms since one of its subforms (the variable to mutate) should not be expanded. It needs another small clause:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:set!</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)))]</span></code></pre><p>The other expressions are harder, since they’re all the binding forms. Fully-expanded Racket code has four local binding forms: <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, and <code>letrec-values</code>. Additionally, as discussed in the previous blog post, <code>local-expand</code> can also produce <code>letrec-syntaxes+values</code> forms produced by local syntax bindings. In the type expander, we completely disallowed runtime bindings from appearing in the resulting program, so we completely removed <code>letrec-syntaxes+values</code> in our expansion, but in the case of handling arbitrary Racket programs, we actually want to leave a <code>letrec-values</code> form behind to hold any runtime bindings (i.e. the <code>values</code> part of <code>letrec-syntaxes+values</code>).</p><p>We’ll start with <code>#%plain-lambda</code>, which is the simplest of all the five aforementioned binding forms. It binds a sequence of identifiers at runtime, and they are in scope within the body of the lambda expression. Just as we created and used an internal-definition context to hold the bindings of a <code>letrec-syntax+values</code> form in the previous blog post, we’ll do the same for Racket’s other binding forms as well:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>However, the above handling of <code>#%plain-lambda</code> isn’t <em>quite</em> right, since the argument list can also include a “rest argument” binding in addition to a sequence of positional arguments. To accommodate this, we can introduce a simple syntax class that handles the different permutations:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">plain-formals</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"formals"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[[</span><span class="n">id</span><span class="w"> </span><span class="mi">1</span><span class="p">]]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">id*:id</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id**:id</span><span class="p">)</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">id*</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">id**</span><span class="p">]]))</span></code></pre><p>Now we can use this to adjust <code>#%plain-lambda</code> to handle rest arguments:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Next, we’ll handle <code>case-lambda</code>. As it turns out, expanding <code>case-lambda</code> is almost exactly the same as expanding <code>#%plain-lambda</code>, except that it has multiple clauses. Since each clause is expanded identically to the body of a <code>#%plain-lambda</code>, and it even has the same shape, the clauses can be extracted into a separate syntax class to share code between the two forms:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]]))</span></code></pre><p>Now, both <code>#%plain-lambda</code> and <code>case-lambda</code> can be handled in a few lines of code each:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>Finally, we need to tackle the three <code>let</code> forms. None of these involve any fundamentally new ideas, but they are a little bit more involved than the variants of lambda due to the need to handle the RHSs. Each variant is slightly different, but not dramatically so: the bindings aren’t in scope when expanding the RHSs of <code>let-values</code>, but they are for <code>letrec-values</code> and <code>letrec-syntaxes+values</code>, and <code>letrec-syntaxes+values</code> creates transformer bindings and must evaluate some RHSs in phase 1 while <code>let-values</code> and <code>letrec-values</code> exclusively bind runtime bindings. It would be possible to implement these three forms in separate clauses, but since we’d ideally like to duplicate as little code as possible, we can write a rather elaborate <code>syntax/parse</code> pattern to handle all three binding forms all at once.</p><p>We’ll start by handling <code>let-values</code> alone to keep things simple:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>This isn’t dramatically different from the implementation of <code>#%plain-lambda</code>. The only difference is that we have to recursively invoke <code>expand-expression</code> on the RHSs in addition to expanding the body expressions. To handle <code>letrec-values</code> in the same clause, however, we’ll have to get a little more creative.</p><p>So far, we haven’t actually tapped very far into <code>syntax/parse</code>’s pattern language over the course of these two blog posts. The full language available to patterns is rather extensive, and we can take advantage of that to write a modification of the above clause that handles both <code>let-values</code> and <code>letrec-values</code> at once:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}}}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span></code></pre><p>The <code>~bind</code> pattern allows us to explicitly control how attributes are bound as part of the pattern-matching process, which allows us to track when we want to enable the recursive binding behavior of <code>letrec-values</code> in our handler code. Since the vast majority of the logic is otherwise identical, this is a significant improvement over duplicating the clause.</p><p>Adding support for <code>letrec-syntaxes+values</code> is done in the same general way, but the pattern is even more involved. In addition to tracking whether or not the bindings are recursive, we have to track if any syntax bindings were present at all, and if they were, bind them with <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span></code></pre><p>This behemoth clause handles all three varieties of <code>let</code> forms that can appear in the result of <code>local-expand</code>. Notably, in the <code>letrec-syntaxes+values</code> case, we expand into <code>letrec-values</code>, since the transformer bindings are effectively erased, and we use <code>syntax-track-origin</code> to record that the result originally came from a use of <code>letrec-syntaxes+values</code>.</p><p>With these five clauses, we’ve handled all the special forms that can appear in expression position in Racket’s kernel language. To tie things off, we just need to handle the cases of a variable reference, which is represented by a bare identifier not bound to syntax, or literal data, like numbers or strings. We can add one more clause at the end to handle those:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span></code></pre><p>Putting them all together, our <code>expand-expression</code> function looks as follows:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="k">quote</span><span class="w"> </span><span class="ss">quote-syntax</span><span class="w"> </span><span class="k">#%top</span><span class="w"> </span><span class="k">#%variable-reference</span><span class="p">}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="k">_</span><span class="p">)</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~and</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="k">#%expression</span><span class="w"> </span><span class="k">#%plain-app</span><span class="w"> </span><span class="k">begin</span><span class="w"> </span><span class="k">begin0</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">with-continuation-mark</span><span class="p">}}</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:#%plain-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">clause.expansion</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[(</span><span class="n">head:case-lambda</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">clause:lambda-clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">clause.expansion</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> + +<span class="w"> </span><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> + +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="n">this-syntax</span><span class="p">])))</span></code></pre><p>If we try it out, we’ll see that it really does work! Even complicated local binding forms are handled properly by our expander:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">))))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">(((</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>We are now able to expand arbitrary Racket expressions in the same way that the expander does. While this might not seem immediately useful—after all, we haven’t actually gained anything here over just calling <code>local-expand</code> with an empty stop list—we can use this as the basis of an expander that can extensibly handle custom core forms, which I may cover in a future blog post.</p><h2><a name="adding-support-for-internal-definitions"></a>Adding support for internal definitions</h2><p>In the previous section, we defined an expander that could expand arbitrary Racket expressions, but our expander is still imperfect: we still do not support internal definitions. For all forms that have bodies, including <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, and <code>letrec-syntaxes+values</code>, Racket permits the use of internal definitions.</p><p>In practice, internal-definition contexts allow for an increased degree of modularity compared to traditional local binding forms, since they provide an <em>extensible</em> binding language. Users may mix many different binding forms within a single definition context, such as <code>define</code>, <code>define-syntax</code>, <code>match-define</code>, and even <code>struct</code>. However, this means the rewriting process described earlier in this blog post is not as simple as detecting the definitions and lifting them into a local binding form, since it’s not immediately apparent which forms are binding forms and which are expressions!</p><p>For this reason, expanding internal-definition contexts happens to be a nontrivial problem in itself. It involves a little more care than expanding expressions does, since it requires using partial expansion to discover which forms are definitions and which forms are expressions. We must take care to never expand too much, but also to expand enough that we reveal all uses of <code>define-values</code> and <code>define-syntaxes</code> (which all definition forms eventually expand into). We also must handle the splicing behavior of <code>begin</code>, which is necessary to allow single forms to expand into multiple definitions.</p><p>We’ll start by writing an <code>expand-body</code> function, which operates similarly to our previous <code>expand-expression</code> function. Unlike <code>expand-expression</code>, <code>expand-body</code> will accept a <em>list</em> of syntax objects, which represents the sequence of forms that make up the body. Logically, each body will create a first-class definition context with <code>syntax-local-make-definition-context</code> to represent the sequence of definitions:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>The bulk of our <code>expand-body</code> function will be a loop that partially expands body forms, adds definitions to the definition context as it discovers them, and returns the expressions and runtime definitions to be rewritten into binding pairs for a <code>letrec-values</code> form. Additionally, the loop will also track so-called <em>disappeared uses</em> and <em>disappeared bindings</em>, which are attached to the expansion using syntax properties to allow tools like DrRacket to learn about the binding structure of phase 1 definitions that are erased as part of macroexpansion.</p><p>The skeleton of this loop is relatively straightforward to write. We will iterate over the syntax objects that make up the body, expand them, and process the expansion using <code>syntax-parse</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">)))))))</span></code></pre><p>The hard part, of course, is actually handling the potential results of that expansion. We need to handle three forms specially: <code>begin</code>, <code>define-values</code>, and <code>define-syntaxes</code>. All other results of partial expansion will be treated as expressions. We’ll start by handling <code>begin</code>, since it’s the simplest case; we only need to prepend the subforms to the list of body forms to be processed, then continue looping:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">)</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>However, as is often the case, this isn’t quite perfect, since the information that these forms came from a surrounding <code>begin</code> is lost, which tools like DrRacket want to know. To solve this problem, the expander adjusts the <code>origin</code> property for all spliced forms, which we can mimic using <code>syntax-track-origin</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This is sufficient for <code>begin</code>, so we can move onto the actual definitions themselves. This actually isn’t too hard, since we just need to add the bindings we discover to the first-class definition context and preserve <code>define-values</code> bindings as binding pairs:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This solution is missing one thing, however, which is the use of <code>syntax-local-identifier-as-binding</code> to any use-site scopes that were added to the binding identifier while expanding the binding form in the definition context. Explaining precisely why this is necessary is outside the scope of this blog post, and is best understood by reading <a href="http://www.cs.utah.edu/plt/scope-sets/pattern-macros.html#%28part._use-site%29">the section on use-site scopes</a> in the paper that describes the theory behind Racket’s current macro system, Bindings as Sets of Scopes. In any case, the impact on our implementation is small:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>Finally, as with <code>begin</code>, we want to track that the binding pairs we generate actually came from a use of <code>define-values</code> (which in turn likely came from a use of some other definition form). Therefore, we’ll add another use of <code>syntax-track-origin</code> to copy and extend the necessary properties:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>That’s it for <code>define-values</code>. All that’s left is to handle <code>define-syntaxes</code>, which is quite similar, but instead of storing the definition in a binding pair, its RHS is immediately evaluated and added to the definition context using <code>syntax-local-bind-syntaxes</code>:</p><pre><code class="pygments"><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span></code></pre><p>As the above snippet indicates, this is also where the disappeared uses and disappeared bindings come in. In previous cases, we’ve used <code>syntax-track-origin</code> to indicate that a piece of syntax was the result of expanding a different piece of syntax, but in this case, <code>define-syntaxes</code> doesn’t expand into anything at all; it’s simply removed from the expansion entirely. Therefore, we need to resort to tracking the information in syntax properties on the resulting <code>letrec-values</code> form, so we’ll save them for later.</p><p>Finally, to finish things up, we can add a catchall clause that handles all other forms, which are now guaranteed to be expressions:</p><pre><code class="pygments"><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span></code></pre><p>This completes our loop that processes definition forms, so all that’s left to do is handle the results. The only significant remaining work is to actually expand the RHSs of the binding pairs we collected and the body expressions, which can be done by calling our own <code>expand-expression</code> function directly:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span></code></pre><p>Finally, we can assemble all the pieces together into a single local binding form with the appropriate syntax properties:</p><pre><code class="pygments"><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))</span></code></pre><p>That’s it. We’ve now written an <code>expand-body</code> function that can process internal definition contexts in the same way that the macroexpander does. Overall, the whole function is just under 45 lines of code:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="p">(</span><span class="nb">gensym</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="n">loop</span><span class="w"> </span><span class="p">([</span><span class="n">stxs</span><span class="w"> </span><span class="n">stxs</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">binding-clauses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">exprs</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-uses</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()]</span> +<span class="w"> </span><span class="p">[</span><span class="n">disappeared-bindings</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">empty?</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">stxs</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:begin</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">append</span><span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">form</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span> +<span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)</span> +<span class="w"> </span><span class="n">exprs</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:define-syntaxes</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">[</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">syntax-local-identifier-as-binding</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="n">exprs</span> +<span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x*</span><span class="p">)</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="k">_</span> +<span class="w"> </span><span class="p">(</span><span class="n">loop</span><span class="w"> </span><span class="p">(</span><span class="nb">rest</span><span class="w"> </span><span class="n">stxs</span><span class="p">)</span><span class="w"> </span><span class="n">binding-clauses</span><span class="w"> </span><span class="p">(</span><span class="nb">cons</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="n">exprs</span><span class="p">)</span> +<span class="w"> </span><span class="n">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)]))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-binding-clauses</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">binding-clause</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="n">binding-clauses</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">binding-clause</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">quasisyntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">[(</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">rhs</span><span class="p">)])])))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expanded-exprs</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="n">exprs</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="o">#,</span><span class="n">expanded-binding-clauses</span><span class="w"> </span><span class="o">#,@</span><span class="n">expanded-exprs</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-uses</span><span class="w"> </span><span class="n">disappeared-uses</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">disappeared-bindings</span><span class="w"> </span><span class="n">disappeared-bindings</span><span class="p">)))))</span></code></pre><p>The next step is to actually use this function. We need to replace certain recursive calls to <code>expand-expression</code> with calls to <code>expand-body</code>, but if we do this naïvely, we’ll have some problems. Currently, when we expand body forms, they’re always immediately inside another definition context (i.e. the bindings introduced by lambda formals or by <code>let</code> binding pairs), but they haven’t actually been expanded in that context yet. When we call <code>expand-body</code>, we create a nested context, which will inherit the bindings, but won’t automatically add the parent context’s scope. Therefore, we need to manually call <code>internal-definition-context-introduce</code> on the body syntax objects before calling <code>expand-body</code>. We can write a small helper function to make this easier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="n">stxs</span><span class="w"> </span><span class="n">ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">ctx</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">add-ctx-scope</span><span class="w"> </span><span class="n">stxs</span><span class="p">))))))</span></code></pre><p>Now we just need to replace the relevant calls to <code>expand-expression</code> with calls to <code>expand-body/in-ctx</code>, starting with a minor adjustment to our <code>lambda-clause</code> syntax class from earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">lambda-clause</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">[</span><span class="n">formals:plain-formals</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">formals.id</span><span class="p">)</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">formals*</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">formals</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">formals*</span><span class="w"> </span><span class="n">body*</span><span class="p">]]))</span></code></pre><p>The only other change must occur in the handling of the various <code>let</code> forms, which similarly replaces <code>expand-expression</code> with <code>expand-body/in-ctx</code>:</p><pre><code class="pygments"><span class="p">[({</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:let-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#f</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">head:letrec-values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#f</span><span class="p">]}}}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~seq</span><span class="w"> </span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="p">{</span><span class="n">~bind</span><span class="w"> </span><span class="p">[</span><span class="n">rec?</span><span class="w"> </span><span class="no">#t</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="n">stxs?</span><span class="w"> </span><span class="no">#t</span><span class="p">]}</span> +<span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">x/s:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">([(</span><span class="n">x:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="p">(</span><span class="n">current-intdef-ctx</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="p">(</span><span class="nb">append*</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">when</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">xs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">x/s</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">rhs/s</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs/s</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">xs/s</span><span class="w"> </span><span class="n">rhs/s</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[[</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">internal-definition-context-introduce</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[[</span><span class="n">x</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">rhs*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rec?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-intdef-ctx</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="n">expand-expression</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">rhs</span><span class="p">)))</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">body*</span><span class="w"> </span><span class="p">(</span><span class="n">expand-body/in-ctx</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">body</span><span class="p">)</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">stxs?</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">syntax/loc</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([(</span><span class="n">x*</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">rhs*</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body*</span><span class="p">)))]</span></code></pre><p>With these changes, we’ve now extended our expression expander with the ability to expand internal definitions. We can see this in action on a simple example:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">42</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">z</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">(</span><span class="nb">make-rename-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">x</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span> +<span class="n">#&lt;syntax</span><span class="w"> </span><span class="p">(</span><span class="k">let-values</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">letrec-values</span><span class="w"> </span><span class="p">([(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">42</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">)))</span><span class="nb">&gt;</span></code></pre><p>Just as we’d like, the transformer bindings were expanded and subsequently eliminated, and the runtime binding was collected into a <code>letrec-values</code> form. The outer <code>let-values</code> is left over from the outer <code>let</code>, which is needed only to create an internal-definition context to hold our internal definitions.</p><h2><a name="putting-the-expression-expander-to-work"></a>Putting the expression expander to work</h2><p>So far, we’ve done a lot of work to emulate the behavior of Racket’s macroexpander, and as the above example demonstrates, we’ve been fairly successful in that goal. However, you might be wondering <em>why</em> we did any of this, as replicating the behavior of <code>local-expand</code> is not very useful on its own. As mentioned above, this can be used as the foundation of an expander for custom core forms that extends, rather than replaces, the built-in Racket core forms, It can also be used to “cheat” and expand through the behavior of the <code>local-expand</code> stop list, which implicitly adds the Racket core forms to any non-empty stop list. Hopefully, I’ll have a chance to cover some of these things more deeply in the future, but for now, I’ll just give a small taste of the latter.</p><p>By using the power of our <code>expand-expression</code> function, it’s actually possible to use this kind of expression expander to do genuinely nefarious things, such as hijack the behavior of arbitrary macros! For example, we could do something evil like make <code>for</code> loops run in reverse order by adding <code>for</code> to <code>current-stop-list</code>, then adding an additional special case to <code>expand-expression</code> for <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">current-stop-list</span><span class="w"> </span><span class="p">(</span><span class="nb">make-parameter</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-values</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">for</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="p">(</span><span class="k">parameterize</span><span class="w"> </span><span class="p">([</span><span class="n">current-context</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">current-expand</span><span class="w"> </span><span class="n">stx</span><span class="p">))</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:literals</span><span class="w"> </span><span class="p">[</span><span class="k">for</span><span class="p">]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">[(</span><span class="n">head:for</span><span class="w"> </span><span class="p">([</span><span class="n">x:id</span><span class="w"> </span><span class="n">seq:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="nb">reverse</span><span class="w"> </span><span class="p">(</span><span class="nb">sequence-&gt;list</span><span class="w"> </span><span class="n">seq</span><span class="p">)))]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; ...</span> +<span class="w"> </span><span class="p">)))</span></code></pre><p>Amazingly, due to the fact that we’ve taken complete control of the expansion process, this will rewrite uses of <code>for</code> <em>even if they are introduced by macroexpansion</em>. For example, we could write a small macro that expands into a use of <code>for</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="nb">in-range</span><span class="w"> </span><span class="n">n</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">i</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">)</span> +<span class="mi">0</span> +<span class="mi">1</span> +<span class="mi">2</span> +<span class="mi">3</span> +<span class="mi">4</span></code></pre><p>If we write a wrapper macro that applies our evil version of <code>expand-expression</code> to its body, then wrap a use of our <code>print-up-to</code> macro with it, it will execute the loop in reverse order:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">form:expr</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">expand-expression</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">form</span><span class="p">)])</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>On its own, this is not that impressive, since we could have just used <code>local-expand</code> on the body directly to achieve this. However, what’s remarkable about <code>hijack-for-loops</code> is that it will work even if the <code>for</code> loop is buried deep inside some arbitrary expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">foo</span> +<span class="w"> </span><span class="p">(</span><span class="n">hijack-for-loops</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">print-up-to</span><span class="w"> </span><span class="n">n</span><span class="p">))))</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span> +<span class="mi">5</span> +<span class="mi">4</span> +<span class="mi">3</span> +<span class="mi">2</span> +<span class="mi">1</span> +<span class="mi">0</span></code></pre><p>Of course, this example is rather contrived—mucking with <code>for</code> loops like this isn’t useful at all, and nobody would really write <code>print-up-to</code> as a macro, anyway—but there is potential for using this technique to do more interesting things.</p><h2><a name="closing-thoughts"></a>Closing thoughts</h2><p>The system outlined in this blog post is not something I would recommend using in any real macro. It is enormously complicated, requires knowledge well above that of your average working macrologist, and it involves doing rather horrible things to the macro system, things it was undoubtably never designed to do. Still, I believe this blog post is useful, for a few different reasons:</p><ol><li><p>The technology outlined in this post, while perhaps not directly applicable to existing real-world problems, provides a framework for implementing various new kinds of syntax transformations in Racket <em>without</em> extending the macro system. It demonstrates the expressive power of the macro system, and it hopefully lays the foundation for a better, more high-level interface for users who wish to define their own languages with custom core forms.</p></li><li><p>This system provides insight into the way the Racket macroexpander operates, <em>in terms of the userspace syntax API</em>. The canonical existing model of hygienic macroexpansion, in the aforementioned <a href="http://www.cs.utah.edu/plt/scope-sets/">Bindings as Sets of Scopes</a> paper, does not explain the workings of internal definition contexts in detail, and it certainly doesn’t explain them in terms that a Racket programmer would already be familiar with. By reencoding those ideas within the macro system itself, an advanced macro writer may be able to more easily connect concepts in the macro system’s implementation to concepts they have already been exposed to.</p></li><li><p>The capability of the proof-of-concept outlined here demonstrates that the limitation imposed by the existing implementation of the stop list (namely, the way it is implicitly extended with additional identifiers) is essentially artificial, and it can be hacked around with sufficient (albeit significant) effort. This isn’t enormously important, but it is somewhat relevant to a recent debate in <a href="https://github.com/racket/racket/issues/2154">a GitHub issue</a> about the handling of the <code>local-expand</code> stop list.</p></li><li><p>Finally, for myself as much as anyone else, this implementation records in a concise way (perhaps overly concise at times) the collection of very subtle details I’ve learned over the past six months about how information is preserved and propagated during the expansion process.</p></li></ol><p>This blog post is not for everybody. If you made it to the end, give yourself a pat on the back. If you made it to the end <em>and</em> understood everything you read: congratulations, you are a certified expert in Racket macro programming. If not, do not fear, and do not lose hope—I plan for something significantly more mellow next time.</p><p>As always, I’d like to give thanks to the people who contributed significantly, if indirectly, to the contents of this blog post, namely <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://mballantyne.net">Michael Ballantyne</a>, and <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a>. And finally, for those interested, all of the code in this blog post can be found in a runnable form <a href="https://gist.github.com/lexi-lambda/c4f4b91ac9c0a555447d72d02e18be7b">in this GitHub gist</a>.</p><ol class="footnotes"></ol></article>Reimplementing Hackett’s type language: expanding to custom core forms in Rackethttps://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/https://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/15 Apr 2018<article><p>In the past couple of weeks, I <a href="https://github.com/lexi-lambda/hackett/commit/ba64193da38f63dab2523f42c1b7614cdfa8c935">completely rewrote the implementation of Hackett’s type language</a> to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.</p><p>This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">my previous blog post on Hackett</a>, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.</p><h2><a name="what-are-core-forms"></a>What are core forms?</h2><p>Before we can get started writing <em>custom core forms</em>, we need to understand the meaning of Racket’s plain old <em>core forms</em>. What is a core form? In order to answer that question, we need to think about how Racket’s expansion and compilation model works.</p><p>To start, let’s consider a simple Racket program. Racket programs are organized into modules, which are usually written with a <code>#lang</code> line at the top. In this case, we’ll use <code>#lang racket</code> to keep things simple:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>How does Racket see this program? Well, before it can do anything with it, it must parse the program text, which is known in Racket as <em>reading</em> the program. The <code>#lang</code> line controls how the program is read—some <code>#lang</code>s provide parsers that allow syntax that is very different from the parser used for <code>#lang racket</code>—but no matter which reader is used, the result is an s-expression (actually a syntax object, but essentially an s-expression) representing a module. In the case of the above program, the result looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="w"> </span><span class="mi">3</span><span class="p">)))</span></code></pre><p>Note the introduction of <code>#%module-begin</code>. Despite the fancy name, this is really just an ordinary macro provided by the <code>racket</code> language. By convention, the reader and expander cooperate to ensure the body of every module is wrapped with <code>#%module-begin</code>; as we’ll see shortly, this allows languages to add functionality that affects the entire contents of the module.</p><p>One the program has been read, it is subsequently <em>expanded</em> by the macroexpander. As the name implies, this is the phase that expands all the macros in a module. What does the above module look like after expansion? Well, it doesn’t look unrecognizable, but it certainly does look different:</p><pre><code class="pygments"><span class="p">(</span><span class="k">module</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">racket</span> +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-module-begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">add2</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">2</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="nb">call-with-values</span> +<span class="w"> </span><span class="p">(</span><span class="k">lambda</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="k">#%plain-app</span><span class="w"> </span><span class="n">add2</span><span class="w"> </span><span class="o">&#39;</span><span class="mi">3</span><span class="p">))</span> +<span class="w"> </span><span class="n">print-values</span><span class="p">)))</span></code></pre><p>Let’s note the things that changed:</p><ol><li><p><code>#%module-begin</code> was replaced with <code>#%plain-module-begin</code>. <code>#%plain-module-begin</code> is a binding that wraps the body of every expanded module, and all definitions of <code>#%module-begin</code> in any language must eventually expand to <code>#%plain-module-begin</code>. However, <code>#lang racket</code>’s <code>#%module-begin</code> doesn’t <em>just</em> expand to <code>#%plain-module-begin</code>, it also wraps bare expressions at the top level of a module so that their results are printed. This is why running the above program prints <code>5</code> even though there is no code related to printing in the original program!</p></li><li><p>The lambda shorthand used with <code>define</code> was converted to an explicit use of <code>lambda</code>, and it was expanded to <code>define-values</code>. In Racket, <code>define</code> and <code>define-syntax</code> are really just macros for <code>define-values</code> and <code>define-syntaxes</code> that only bind a single identifier.</p></li><li><p>All function applications were tagged explicitly with <code>#%plain-app</code>. This syntactically distinguishes function applications from uses of forms like <code>define-values</code> or <code>lambda</code>. It also allows languages to customize function application by providing their own macros named <code>#%app</code> (just like languages can provide their own macros named <code>#%module-begin</code> that expand to <code>#%plain-module-begin</code>), but that is outside the scope of this blog post.</p></li><li><p>All literals have been wrapped with <code>quote</code>, so <code>2</code> became <code>'2</code> and <code>3</code> became <code>'3</code>.</p></li></ol><p>Importantly, the resulting program contains <strong>no macros</strong>. Such programs are called <em>fully expanded</em>, since all macros have been eliminated and no further expansion can take place.</p><p>So what’s left behind? Well, some of the things in the program are literal data, like the numbers <code>2</code> and <code>3</code>. There are also some variable references, <code>x</code> and <code>add2</code>. Most of the program, however, is built out of primitives like <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, and <code>lambda</code>. These primitives are <em>core forms</em>—they are not variables, since they do not represent bindings that contain values at runtime, but they are also not macros, since they cannot be expanded any further.</p><p>In this sense, a fully-expanded program is just like a program in most languages that do not have macros. Core forms in Racket correspond to the syntax of other languages. We can imagine a JavaScript program similar to the above fully-expanded Racket program:</p><pre><code class="pygments"><span class="n">var</span> <span class="n">add2</span> <span class="o">=</span> + <span class="n">function</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span> <span class="p">};</span> + +<span class="n">console</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">add2</span><span class="p">(</span><span class="mi">3</span><span class="p">));</span></code></pre><p>Just as this JavaScript program is internally transformed into an AST containing a definition node, a function abstraction node, and some function application nodes, a fully-expanded Racket program represents an AST ready to be sent off to be <em>compiled</em>. The Racket compiler has built-in rules for how to compile core forms like <code>define-values</code>, <code>lambda</code>, and <code>#%plain-app</code>, and the result is optimized Racket bytecode.</p><p>In the remainder of this blog post, as most discussions of macros do, we’ll ignore the <em>read</em> and <em>compile</em> steps of the Racket program pipeline and focus exclusively on the <em>expand</em> step. It’s useful, however, to keep the other steps in mind, since we’re going to be discussing what it means to implement custom core forms, and core forms really only make sense in the context of the subsequent compilation step that consumes them.</p><h3><a name="racket-s-default-core-forms"></a>Racket’s default core forms</h3><p>So, now that we know what core forms are in an abstract sense, what are they in practice? We’ve already encountered <code>module</code>, <code>#%plain-module-begin</code>, <code>#%plain-app</code>, <code>define-values</code>, <code>lambda</code>, and <code>quote</code>, but there are many more. The full list is available in the section of the Racket reference named <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29">Fully Expanded Programs</a>, and I will not list all of them here. In general, they are more or less what you’d expect. The list of Racket’s core forms also includes things like <code>define-syntaxes</code>, <code>if</code>, <code>let-values</code>, <code>letrec-values</code>, <code>begin</code>, <code>quote-syntax</code>, and <code>set!</code>. Fundamentally, these correspond to the basic operations the Racket compiler understands, and it allows the remainder of Racket’s compilation pipeline to ignore the complexities of macroexpansion.</p><p>These forms are fairly versatile, and it’s easy to build high-level abstractions on top of them. For example, <code>#lang racket</code> implements <code>cond</code> as a macro that eventually expands into <code>if</code>, and it implements <code>syntax</code> as a macro that eventually expands into function calls and <code>quote-syntax</code>. The real power comes in the way new macros can be built out of other macros, not just core forms, so Racket’s <code>match</code> can expand into uses of <code>let</code> and <code>cond</code>, and it doesn’t need to concern itself with using <code>let-values</code> and <code>if</code>. For this reason, Racket’s core forms are quite capable of representing any language imaginable, since fully-expanded programs are essentially instructions for the Racket virtual machine, and macros are mini-compilers that can be mixed and matched.</p><h3><a name="the-need-for-custom-core-forms"></a>The need for custom core forms</h3><p>With that in mind, why might we wish to define <em>custom</em> core forms? In fact, what would such a thing even mean? By their very nature, <em>all</em> Racket programs eventually expand into Racket’s core forms; new core forms cannot be added because Racket’s underlying compiler infrastructure is not (currently) extensible. New forms can be added that are defined in terms of other forms, but adding new primitives doesn’t make any sense, since the compiler would not know what to do with them.</p><p>Despite this, there <em>are</em> at least two use-cases in which a programmer might wish to customize the set of core forms produced by the macroexpander. Each situation is slightly different, but they both revolve around the same idea.</p><h4><a name="supporting-multiple-backends"></a>Supporting multiple backends</h4><p>The most commonly discussed use case for customizing the set of core forms is for languages that wish to use the Racket macroexpander, but target backends that are not the Racket compiler. For example, a user might implement a Racket <code>#lang</code> that describes electronic circuits, and they might even implement a way to execute such a program in Racket, but they might <em>also</em> wish to compile the result to a more traditional hardware description language. Like other languages in the Racket ecosystem, such a language would be made up of a tower of macros built on top of core forms; unlike other languages, the core forms might need to be more abstract than the ones provided by Racket to efficiently compile to other targets.</p><p>In the case of a hardware description language, the custom core forms might include things like <code>input</code> and <code>output</code> for declaring circuit inputs and outputs, and expressions might be built out of hardware operations rather than high-level things like function calls. The Racket macroexpander would expand the input program into the custom set of core forms, at which point an external compiler program could compile the resutling AST in a more traditional way. If the language author wished, they could <em>additionally</em> define implementations of these core forms as Racket macros that eventually expand into Racket, which would allow them to emulate their circuits in Racket at little cost, but this would be a wholly optional step.</p><p>Essentially, this use case stems from a desire to reuse Racket’s advanced language-development technology, such as the macroexpander, the module system, and editor tooling, without also committing to using Racket as a runtime, which is not always appropriate for all languages. This use case is not nearly as easy as it ought to be, but it is a common request, and it is possible that future improvements to the Racket toolchain will be designed specifically to address this problem.</p><h4><a name="compiling-an-extensible-embedded-language"></a>Compiling an extensible embedded language</h4><p>A second use case for custom core forms is less frequently discussed, but I think it might actually be significantly more common in practice were it available in a form accessible to working macro programmers. In this scenario, users might wish to remain within Racket, but still want to define a custom language that other macros can consume.</p><p>This concept is a little more vague and fuzzily-defined than the case of developing a separate backend, so allow me to propose an example. Imagine a Racket programmer decides to build an embedded DSL for asynchronously producing and consuming events, similar to first-order functional reactive programming. In this case, the DSL is designed to be used in larger Racket programs, so it <em>will</em> eventually expand to Racket’s core forms. However, it’s possible that such a language might wish to enforce static invariants about the network graph, and in doing so, it might be able to produce significantly more optimal Racket code via a compile-time analysis.</p><p>Performing such a compile-time analysis is essentially writing a custom optimizer as part of a macro, which has been done numerous times already within the Racket ecosystem. One of the most prominent examples of such a thing is the <code>match</code> macro, which parses users’ patterns into compile-time data structures, performs a fairly traditional optimization pass designed to efficiently compile pattern matching, and it emits optimized Racket code as a result. This approach works well for fairly contained problems like pattern-matching, but it works less well for entirely new embedded languages that include everything from their own notion of evaluation to their own binding forms.</p><p>Existing DSLs of this type are rare, but they do exist. <code>syntax/parse</code> provides an expressive, specialized pattern-matching language designed specifically for matching syntax objects, and it uses a different model from <code>racket/match</code> to be more suitable for that task. It allows backtracking with cuts, an extensible pattern language, an abstraction language for defining reusable parsers that can accept inputs and produce outputs, and fine-grained control over both parsing and binding. While <code>match</code> is essentially just a traditional pattern-matcher, albeit an extensible one, <code>syntax-parse</code> is its own programming language, closer in some ways to Prolog than to Racket.</p><p>For this reason, <code>syntax/parse</code> has an extensive language to do everything from creating new bindings to controlling when and how parsing fails. This language is represented in two ways: an inline pattern language, and an alternate syntax known as <a href="http://docs.racket-lang.org/syntax/stxparse-specifying.html#%28part._.Pattern_.Directives%29"><em>pattern directives</em></a>. Here is an example of pattern directives in action, from my own <code>threading</code> library:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>Each directive is represented by a keyword, in this case <code>#:do</code> and <code>#:with</code>. Each directive has a corresponding keyword in the pattern language, in this case <code>~do</code> and <code>~parse</code>. Therefore, the above pattern could equivalently be written this way:</p><pre><code class="pygments"><span class="p">[{</span><span class="n">~and</span><span class="w"> </span><span class="p">(</span><span class="k">_</span><span class="w"> </span><span class="n">ex:expr</span><span class="w"> </span><span class="n">cl:clause</span><span class="w"> </span><span class="n">remaining:clause</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">~do</span><span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">~parse</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)}}</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>The transformation can go in the other direction, too—each syntax class annotation on each pattern variable can be extracted into the directive language using <code>#:declare</code>, so this is also equivalent:</p><pre><code class="pygments"><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">expr</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">cl</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:declare</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="n">clause</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-&gt;list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl.call</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-values</span><span class="w"> </span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="n">post</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">split-at</span><span class="w"> </span><span class="n">call</span><span class="w"> </span><span class="p">(</span><span class="nb">add1</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">cl.insertion-point</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">))))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">pre</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="n">post</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">pre</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">ex</span><span class="w"> </span><span class="n">post</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">cl</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">adjust-outer-context</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="n">app/ctx</span><span class="w"> </span><span class="n">remaining</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">)]</span></code></pre><p>This is very much a programming language, but it has very different semantics from programming in Racket! Failure to match against a <code>#:with</code> or <code>~parse</code> pattern causes pattern-matching to backtrack, and though it’s possible to escape to Racket using <code>#:do</code> or <code>~do</code>, practical uses of <code>syntax/parse</code> really do involve quite a lot of programming in its pattern DSL.</p><p>But the Racket programmer might not find this DSL wholly satisfying. Why? Well, it isn’t extensible! The pattern directives—<code>#:declare</code>, <code>#:do</code>, and <code>#:with</code>, among others—are essentially the core forms of <code>syntax/parse</code>’s pattern-matching language, but new ones cannot be defined. The desire to make this language easy to analyze statically in order to emit optimal pattern-matching code meant its author opted to define the language in terms of a specific grammar rather than a tower of macros.</p><p>But what if <code>syntax/parse</code> could define its own core forms? What if, instead of <code>#:do</code>, <code>#:declare</code>, and <code>#:with</code> being implemented as keyword options specially recognized by the <code>syntax-parse</code> grammar, it defined <code>do</code>, <code>declare</code>, and <code>with</code> as core forms for a new, macro-enabled language? A user of the language could then define a completely ordinary Racket macro and use it with this new language as long as it eventually expanded into the <code>syntax/parse</code> core forms. The implementation of <code>syntax/parse</code> could then invoke the macroexpander to request each clause be expanded into its core forms, perform its static analysis on the result, and finally emit optimized Racket code.</p><p>Now, to be fair, <code>syntax/parse</code> is not actually entirely inextensible. While new directives cannot be defined, new patterns can be added through a pattern-expander API that was added to the library after its initial design. However, pattern expanders are still not ideal because they are not ordinary Racket macros—users must explicitly define each pattern expander differently from how they would a macro—and they cannot use existing Racket forms, even ones that would theoretically be compatible with an arbitrary set of core forms.</p><p>The technique described in this blog post avoids all those problems. In the following sections, I’ll show that it’s possible to define an embedded language with a custom set of core forms that works well with the rest of the Racket ecosystem and still permits arbitrary static analysis.</p><h2><a name="the-need-for-a-custom-type-language-in-hackett"></a>The need for a custom type language in Hackett</h2><p>In the previous section, I described two use cases for custom core forms. Hackett, in fact, has uses for <em>both</em> of them:</p><ul><li><p>Hackett can definitely make use of custom core forms to compile to multiple backends. Eventually, it would be nice to compile Hackett to an intermediate language that can target both the Racket runtime and Haskell or GHC Core. This would allow Hackett to take advantage of GHC’s advanced optimizing compiler that already has decades of tuning for a pure, lazy, functional programming language, at the cost of not having access to the rest of Racket’s ecosystem of libraries at runtime.</p></li><li><p>Hackett can <em>also</em> make use of custom core forms for an embedded DSL. In this case, that embedded DSL is actually Hackett’s type language.</p></li></ul><p>The second of those two use cases is simpler, and it’s what I ended up implementing first, so it’s what I will focus on in this blog post. Hackett’s type language is fundamentally quite simple, so its set of custom core forms is small as well. Everything in the type language eventually compiles into only seven core forms:</p><ul><li><p><code>(#%type:con <em>id</em>)</code> — Type constructors, like <code>Integer</code> or <code>Maybe</code>. These are one of the fundamental building blocks of Hackett types.</p></li><li><p><code>(#%type:app <em>type</em> <em>type</em>)</code> — Type application, such as <code>(Maybe Integer)</code>. Types are curried, so type constructors that accept multiple arguments are represented by nested uses of <code>#%type:app</code>.</p></li><li><p><code>(#%type:forall <em>id</em> <em>type</em>)</code> — Universal quantification. This is essentially a binding form, which binds any uses of <code>(#%type:bound-var <em>id</em>)</code> in <code><em>type</em></code>.</p></li><li><p><code>(#%type:qual <em>type</em> <em>type</em>)</code> — Qualified types, aka types with typeclass constraints. Constraints in Hackett, like in GHC, are represented by types, so typeclass names like <code>Eq</code> are bound as type constructors.</p></li><li><p>Finally, Hackett types support three different varieties of type variables:</p><ul><li><p><code>(#%type:bound-var <em>id</em>)</code> — Bound type variables. These are only legal under a corresponding <code>#%type:forall</code>.</p></li><li><p><code>(#%type:wobbly-var <em>id</em>)</code> — Solver variables, which may unify with any other type as part of the typechecking process.</p></li><li><p><code>(#%type:rigid-var <em>id</em>)</code> — Rigid variables, aka skolem variables, which only unify with themselves. They represent a unique, anonymous type used to ensure types are suitably polymorphic.</p></li></ul></li></ul><p>To implement our custom core forms in Racket, we need to somehow define them, but how? Intentionally, these should never be expanded, since we want the expander to stop expanding whenever it encounters one of these identifiers. While we can’t encode this directly, we <em>can</em> bind them to macros that do nothing but raise an exception if something attempts to expand them:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span></code></pre><p>This will ensure our core forms are never accidentally expanded, and we’ll instruct the macroexpander to stop whenever it sees one of them via a separate mechanism.</p><h3><a name="expanding-types-in-our-type-language"></a>Expanding types in our type language</h3><p>We’ve now defined our core forms, but we’ve intentionally left them meaningless. How do we actually inform the expander about how our types ought to be expanded? While it’s true that we don’t want the core forms themselves to be eliminated, we <em>do</em> want to expand some of their subforms. For example, in the type <code>(#%type:app a b)</code>, we want to recursively expand <code>a</code> and <code>b</code>.</p><p>In order to do this, we’ll use the API made available by the expander for manually invoking macroexpansion from within another macro. This API is called <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29"><code>local-expand</code></a>, and it has an option relevant to our needs: the stop list.</p><p>Often, <code>local-expand</code> is used to force the expander to completely, recursively expand a form. For example, by using <code>local-expand</code>, we can produce a fragment of a fully-expanded program from a piece of syntax that still includes macros:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">])</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="c1">; =&gt; (let-values ([(x) &#39;1]) (#%plain-app + x &#39;2))</span></code></pre><p>The third argument to <code>local-expand</code> is the <em>stop list</em>, which controls how deep the expander ought to expand a given form. By providing an empty list, we ask for a complete, recursive expansion. In this case, however, we don’t want a complete expansion! We can inform the expander to stop whenever it sees any of our custom core forms by passing a list of our core form identifiers instead of an empty list:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">))</span> +<span class="w"> </span><span class="c1">; =&gt; (#%type:forall x t)</span></code></pre><p>Of course, this isn’t very interesting, since it just gives us back exactly what we gave it. It spotted the <code>#%type:forall</code> identifier, which is in our stop list, and immediately halted expansion. It didn’t attempt to continue expanding <code>t</code> since the expander has no way of knowing which pieces of <code>(#%type:forall x t)</code> it should expand! In this case, we want it to recur to expand <code>t</code>, since it should be a type, but not <code>x</code>, since <code>#%type:forall</code> essentially puts <code>x</code> in binding position.</p><p>Therefore, we have to get more clever. We need to call <code>local-expand</code> to produce a type, then we have to pattern-match on it and subsequently call <code>local-expand</code> <em>again</em> on any of the pieces of syntax we want to keep expanding. Eventually, we’ll run out of things to expand, and our type will be fully-expanded.</p><p>One good way to do this is to use <code>syntax/parse</code> syntax classes, since they provide a convenient way for other macros to invoke the type expander. To implement our type expander, we’ll use two mutually recursive syntax classes: one to perform the actual expansion using <code>local-expand</code> and a second to pattern-match on the resulting expanded type. For example, here’s what these two classes would look like if they only handled <code>#%type:con</code> and <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">:expanded-type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]))</span></code></pre><p>This blog post is definitely <em>not</em> a <code>syntax/parse</code> tutorial, so I will not explain in detail everything that’s going on here, but the gist of it is that the above code defines two syntax classes, both of which produce a single output attribute named <code>expansion</code>. This attribute contains the fully expanded version of the type currently being parsed. In the <code>#%type:con</code> case, <code>expansion</code> is just <code>this-syntax</code>, which holds the current piece of syntax being parsed. This makes sense, since uses of <code>#%type:con</code> just expand to themselves—expanding <code>(#%type:con Maybe)</code> should not perform any additional expansion on <code>Maybe</code>. This is one of Hackett’s atomic types.</p><p>In contrast, <code>#%type:app</code> <em>does</em> recursively expand its arguments. By annotating its two subforms with <code>:type</code>, the <code>type</code> syntax class will invoke <code>local-expand</code> on each subform, which will in turn use <code>expanded-type</code> to parse the resulting type. This is what implements the expansion loop that will eventually expand each type completely. Once <code>a</code> and <code>b</code> have been expanded, <code>#%type:app</code> reassembles them into a new syntax object using <code>#'(#%type:app a.expansion b.expansion)</code>, which replaces their unexpanded versions with their new, expanded versions.</p><p>We can see this behavior by writing a small <code>expand-type</code> function that will expand its argument:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>Now we can use it to observe what happens when we try expanding a type using <code>#%type:app</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; #%type:app: expected type</span> +<span class="c1">; at: Maybe</span> +<span class="c1">; in: (#%type:app Maybe Integer)</span></code></pre><p>Okay, it failed with an error, which is not ideal, but it makes sense. We haven’t actually defined <code>Maybe</code> or <code>Integer</code> anywhere. Let’s do so! We can define them as simple macros that expand into uses of <code>#%type:con</code>, which can be done easily using <a href="http://docs.racket-lang.org/syntax/transformer-helpers.html#%28def._%28%28lib._syntax%2Ftransformer..rkt%29._make-variable-like-transformer%29%29"><code>make-variable-like-transformer</code></a> from <code>syntax/transformer</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Maybe</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Integer</span><span class="p">)))</span></code></pre><p>Now, if we try expanding that same type again:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Integer))</span></code></pre><p>…it works! Neat. Now we just need to add the cases for the remaining forms in our type language:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">expanded-type</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="n">t:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">a:type</span><span class="w"> </span><span class="n">b:type</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>This is pretty good already, and to a first approximation, it’s done! However, it doesn’t actually work as well as we’d really like it to. One of the whole points of doing things this way is to allow other macros like <code>let-syntax</code> to work in types. For example, we ought to be able to create a local type binding with <code>let-syntax</code> and have it just work. Unfortunately, it doesn’t:</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; let-syntax: expected one of these identifiers: `#%type:con&#39;, `#%type:app&#39;, `#%type:forall&#39;, `#%type:qual&#39;, `#%type:bound-var&#39;, `#%type:wobbly-var&#39;, or `#%type:rigid-var&#39;</span> +<span class="c1">; at: letrec-syntaxes+values</span> +<span class="c1">; in: (let-syntax ((Bool (make-variable-like-transformer (syntax Bool)))) (#%type:app Maybe Bool))</span></code></pre><p>What went wrong? And why is it complaining about <code>letrec-syntaxes+values</code>? Well, if you read the documentation for <code>local-expand</code>, you’ll find that its behavior is a little more complicated than you might at first believe:</p><blockquote><p>If <em><code>stop-ids</code></em> is [a nonempty list containing more than just <code>module*</code>], then <code>begin</code>, <code>quote</code>, <code>set!</code>, <code>#%plain-lambda</code>, <code>case-lambda</code>, <code>let-values</code>, <code>letrec-values</code>, <code>if</code>, <code>begin0</code>, <code>with-continuation-mark</code>, <code>letrec-syntaxes+values</code>, <code>#%plain-app</code>, <code>#%expression</code>, <code>#%top</code>, and <code>#%variable-reference</code> are implicitly added to <em><code>stop-ids</code></em>. Expansion stops when the expander encounters any of the forms in <em><code>stop-ids</code></em>, and the result is the partially-expanded form.</p></blockquote><p>That’s a little strange, isn’t it? I am not completely sure why the behavior works quite this way, though I’m sure backwards compatibility plays a significant part, but while some of the behavior seems unnecessary, the issue with <code>letrec-syntaxes+values</code> (which <code>let-syntax</code> expands to) is a reasonable one. If the expander naïvely expanded <code>letrec-syntaxes+values</code> in the presence of a nonempty stop list, it could cause some significant problems!</p><p>Allow me to illustrate with an example. Let’s imagine we are the expander, and we are instructed to expand the following program:</p><pre><code class="pygments"><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">))</span></code></pre><p>We see <code>let-syntax</code>, so we start by evaluating the expression on the right hand side of the <code>Bool</code> binding. This produces a transformer expression, so we bind <code>Bool</code> to the transformer in the local environment, then move onto expanding the body. At this point, the expander is looking at this:</p><pre><code class="pygments"><span class="c1">; local bindings:</span> +<span class="c1">; Bool -&gt; #&lt;variable-like-transformer&gt;</span> +<span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)</span></code></pre><p>Now, the identifier in application position is <code>#%type:app</code>, and <code>#%type:app</code> is in the stop list. Therefore, expansion must stop, and it does not attempt to expand any further. But what should the result of expansion be? Well, the <code>let-syntax</code> needs to go away when we expand it—local syntax bindings are erased as part of macroexpansion—so the logical thing to expand into is <code>(#%type:app Maybe Bool)</code>. But this is a problem, because when we then go to expand <code>Bool</code>, <code>Bool</code> isn’t in the local binding table anymore! The <code>let-syntax</code> was already erased, and <code>Bool</code> is unbound!</p><p>When expanding recursively, this isn’t a problem, since the entire expression is guaranteed to be expanded while the local binding is still in the expander’s environment. As soon as we introduce partial expansion, however, we run the risk of a binding getting erased too early. So we’re stuck: we can’t recursively expand, or we’ll expand too much, but we can’t partially expand, since we might expand too little.</p><p>Confronted with this problem, there is some good news and some bad news. The good news is that, while the macroexpander can’t help us, we can help the macroexpander by doing some of the necessary bookkeeping for it. We can do this using first-class definition contexts, which allow us to manually extend the local environment when we call <code>local-expand</code>. The bad news is that first-class definition contexts are <em>complicated</em>, and using them properly is a surprisingly subtle problem.</p><p>Fortunately, I’ve already spent a lot of time figuring out what needs to be done to properly manipulate the necessary definition contexts in this particular situation. The first step is to parameterize our <code>type</code> and <code>expanded-type</code> syntax classes so that we may thread a definition context around as we recursively expand:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:qual</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now, we can add an additional case to <code>expanded-type</code> to handle <code>letrec-syntaxes+values</code>, which will explicitly create a new definition context, add bindings to it, and use it when parsing the body:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>But even this isn’t quite right. The problem with this implementation is that it throws away the existing <code>intdef-ctx</code> argument to <code>expanded-type</code>, which means those bindings will be lost as soon as we introduce a new set. To fix this, we have to make the new definition context a <em>child</em> of the previous definition context by passing the old context as an argument to <code>syntax-local-make-definition-context</code>. This will ensure the parent bindings are brought into scope when expanding using the child context:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span></code></pre><p>With this in place, our example using <code>let-syntax</code> actually works!</p><pre><code class="pygments"><span class="p">(</span><span class="n">expand-type</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">Bool</span><span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">Bool</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="n">#%type:app</span><span class="w"> </span><span class="n">Maybe</span><span class="w"> </span><span class="n">Bool</span><span class="p">)))</span> +<span class="c1">; =&gt; (#%type:app (#%type:con Maybe) (#%type:con Bool))</span></code></pre><p>Pretty cool, isn’t it?</p><h3><a name="preserving-syntax-properties-and-source-locations"></a>Preserving syntax properties and source locations</h3><p>We’ve now managed to essentially implement an expander for our custom language by periodically yielding to the Racket macroexpander, and for the most part, it works. However, our implementation isn’t perfect. The real Racket macroexpander takes great care to preserve source locations and syntax properties on syntax objects wherever possible, which our implementation does not do. Normally we don’t have to worry so much about such things, since the macroexpander automatically copies properties when expanding macros, but since we’re circumventing the expander, we don’t get that luxury. In order to properly preserve this information, we’ll have to be a little more careful.</p><p>To start, we really ought to copy the identifier in application position into the output wherever we can. In addition to preserving source location information and syntax properties, it also preserves the even more visible renamings. For example, if a user imports <code>#%type:app</code> under a different name, like <code>#%type:apply</code>, we should expand to a piece of syntax that still has <code>#%type:apply</code> in application position instead of replacing it with <code>#%type:app</code>.</p><p>To do this, we just need to bind each of the identifiers in application position, then use that binding when we produce output. For example, we would adjust the <code>#%type:app</code> clause to the following:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">)]</span></code></pre><p>But even after doing this, some source locations and syntax properties are lost, since we’re still reconstructing the pair from scratch. To ensure we copy <em>everything</em>, we can define two helper macros, <code>syntax/loc/props</code> and <code>quasisyntax/loc/props</code>, which are like <code>syntax/loc</code> and <code>quasisyntax/loc</code> but copy properties in addition to source location information:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span></code></pre><p>Using <code>syntax/loc/props</code>, we can be truly thorough about ensuring all properties are preserved:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span></code></pre><p>Applying this to the other relevant clauses, we get an updated version of the <code>expanded-type</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="k">letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]))</span></code></pre><p>Now we’re getting closer, but if you can believe it, even <em>this</em> isn’t good enough. The real expander’s implementation of <code>letrec-syntaxes+values</code> does two things our implementation does not: it copies properties and updates the <code>'origin</code> property to indicate the syntax came from a use of <code>letrec-syntaxes+values</code>, and it adds a <code>'disappeared-use</code> property to record the erased bindings for use by tools like DrRacket. We can apply <code>syntax-track-origin</code> and <code>internal-definition-context-track</code> to the resulting syntax to add the same properties the expander would:</p><pre><code class="pygments"><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span></code></pre><p>Now we’ve <em>finally</em> dotted all our i’s and crossed our t’s. While it does take a lot to properly emulate what the macroexpander is doing, the important thing is that it’s actually possible! The end result of all this definition context juggling and property copying is that we’ve effectively managed to move some of the macroexpander’s logic into userspace code, which allows us to manipulate it as we see fit.</p><h3><a name="connecting-our-custom-language-to-hackett"></a>Connecting our custom language to Hackett</h3><p>It took a lot of work, but we finally managed to write a custom type language, and while the code is not exactly simple, it’s not actually very long. The entire implementation of our custom type language is less than 80 lines of code:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-meta</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/parse</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">racket/base</span> +<span class="w"> </span><span class="n">syntax/intdef</span> +<span class="w"> </span><span class="n">threading</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">quasisyntax/loc/props</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">()</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">syntax-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">from-stx-expr:expr</span><span class="w"> </span><span class="p">{</span><span class="n">~describe</span><span class="w"> </span><span class="s2">"template"</span><span class="w"> </span><span class="n">template</span><span class="p">})</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx-expr</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax?</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-argument-error</span><span class="w"> </span><span class="o">&#39;#,</span><span class="ss">name</span><span class="w"> </span><span class="s2">"syntax?"</span><span class="w"> </span><span class="n">from-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">stx</span><span class="w"> </span><span class="p">(</span><span class="o">#,</span><span class="n">syntax-id</span><span class="w"> </span><span class="n">template</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-disarm</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="no">#f</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-rearm</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">stx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">stx*</span><span class="p">)</span><span class="w"> </span><span class="n">from-stx</span><span class="w"> </span><span class="n">from-stx</span><span class="p">)</span><span class="w"> </span><span class="n">stx</span><span class="p">)))]))</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">syntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">syntax</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-syntax/loc/props</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">quasisyntax/loc/props</span><span class="w"> </span><span class="o">#&#39;</span><span class="k">quasisyntax</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntaxes</span><span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">type-literal</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="s2">"cannot be used as an expression"</span><span class="w"> </span><span class="n">stx</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span> +<span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="w"> </span><span class="n">type-literal</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-literal-ids</span> +<span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:con</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:app</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:forall</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">#%type:rigid-var</span><span class="p">))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-literal-set</span><span class="w"> </span><span class="n">type-literals</span> +<span class="w"> </span><span class="p">[</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">#%type:app</span><span class="w"> </span><span class="n">#%type:forall</span><span class="w"> </span><span class="n">#%type:qual</span> +<span class="w"> </span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">#%type:rigid-var</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="p">[</span><span class="n">intdef-ctx</span><span class="w"> </span><span class="no">#f</span><span class="p">])</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"type"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">||</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span> +<span class="w"> </span><span class="p">(</span><span class="nb">local-expand</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">expression</span><span class="w"> </span><span class="n">type-literal-ids</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)])</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="p">(</span><span class="n">expanded-type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">expansion</span><span class="p">]</span> +<span class="w"> </span><span class="kd">#:commit</span> +<span class="w"> </span><span class="kd">#:literal-sets</span><span class="w"> </span><span class="p">[</span><span class="n">kernel-literals</span><span class="w"> </span><span class="n">type-literals</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:letrec-syntaxes+values</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">([(</span><span class="n">id:id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">e:expr</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="n">t:expr</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-make-definition-context</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">for</span><span class="w"> </span><span class="p">([</span><span class="n">ids</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nb">in-list</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">e</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-bind-syntaxes</span><span class="w"> </span><span class="n">ids</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">))]</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t*</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="p">)}</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">~&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">internal-definition-context-track</span><span class="w"> </span><span class="n">intdef-ctx*</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t*.expansion</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">syntax-track-origin</span><span class="w"> </span><span class="n">this-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">head</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:con</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:app</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:forall</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">x:id</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">t.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">head:#%type:qual</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)}</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="w"> </span><span class="n">intdef-ctx</span><span class="p">)})</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="p">(</span><span class="n">syntax/loc/props</span><span class="w"> </span><span class="n">this-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">a.expansion</span><span class="w"> </span><span class="n">b.expansion</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:bound-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:wobbly-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">#%type:rigid-var</span><span class="w"> </span><span class="n">~!</span><span class="w"> </span><span class="n">_:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">expansion</span><span class="w"> </span><span class="n">this-syntax</span><span class="p">])</span> + +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">expand-type</span><span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span><span class="w"> </span><span class="p">[</span><span class="n">t:type</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">t.expansion</span><span class="p">])))</span></code></pre><p>But what now? Just as Racket fully-expanded programs are useless without a compiler to turn them into something useful, our custom type language doesn’t do anything at all in isolation. As it happens, in the case of the type language, we don’t have a compiler at all—we have a <em>typechecker</em>. The Hackett typechecker consumes fully-expanded types as input and uses them to perform its typechecking process. The actual implementation of Hackett’s typechecker is outside the scope of this blog post, since it’s really an entirely separate problem, but you can probably imagine what such a thing might look like, in an extremely vague, handwavy sense.</p><p>But we don’t <em>just</em> need a typechecker. Just as the authors of Racket don’t expect users to write programs using the core forms directly, we also don’t expect users to write their types using the fully-expanded syntax. If we did, all this fancy expansion machinery would be pretty pointless! Hackett provides a custom <code>#%app</code> binding that converts n-ary type applications to nested uses of <code>#%type:app</code>, as well as a nicer <code>forall</code> macro that supports specifying multiple type variables and multiple typeclass constraints all at once. The best part, though, is that these macros can be defined in a completely straightforward way, just as any ordinary Racket macro would be written, and the machinery will work precisely as intended. It’s also perfectly okay to have two different versions of <code>#%app</code>—one for types and one for values—since <a href="/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/">Hackett supports multiple namespaces</a>, and each can have its own <code>#%app</code> binding.</p><p>The real implementation of Hackett’s type language is a little bit longer than the one in this blog post because it includes some extra definitions to provide custom <code>syntax/parse</code> pattern expanders for matching types and some template metafunctions for producing them, which are used by the typechecker, but if you’d like to see the whole thing, <a href="https://github.com/lexi-lambda/hackett/blob/ba64193da38f63dab2523f42c1b7614cdfa8c935/hackett-lib/hackett/private/type-language.rkt">it’s available on GitHub here</a>.</p><h2><a name="evaluation-limitations-and-acknowledgements"></a>Evaluation, limitations, and acknowledgements</h2><p>Reimplementing Hackett’s type language took about a week and a half, about half of which was supplemented by the extra time I had before I started <a href="https://twitter.com/lexi_lambda/status/976533916596097024">my new job</a> this past week. A portion of that time was spent deciding what I actually wanted to do, and a lot of it was spent hunting down fiddly bugs. All told, the rewrite resulted in a net addition of 250 lines of code to the Hackett codebase. However, 350 of the added lines reside in a new, self-contained module dedicated to Hackett’s type language, so the change actually resulted in a net <em>removal</em> of 100 lines from the rest of the codebase, which I consider an organizational win.</p><p>As for whether or not the change will accomplish the goals I had in mind, I think signs currently point to a strong likelihood of the answer being yes. The very same night I finalized and merged the changes to the type language, I dusted off an old prototype of typeclass deriving I had not been able to get working due to insufficiencies of the old type representation. Not only was I <a href="https://twitter.com/lexi_lambda/status/985051504867446786">able to get it working</a> quickly and easily, I was able to do it in <a href="https://twitter.com/lexi_lambda/status/985052476473856000">no more than 20 lines of code</a>. While the implementation is not as robust as it should ideally be, nor is it safe or simple enough yet to be easy for Hackett users to write themselves, making the impossible possible is usually a sign of motion in the right direction.</p><p>Unfortunately, the technique outlined in this blog post is not completely flawless. Due to its reliance on the <code>local-expand</code> stop list, this technique is incompatible with macros that force recursive expansion using an empty stop list. In the upcoming reimplementation of the Racket macroexpander to be released in Racket 7, this includes <code>syntax-parameterize</code>, which unfortunately means syntax parameters don’t work in the type language. This is a problem, and while it’s not a dealbreaker, it is something that will almost certainly have to be fixed at some point. Fortunately, it isn’t intractable, and I’ve been discussing some potential approaches to fixing the problem, whether via changes to the macroexpander or by making macros like <code>syntax-parameterize</code> cooperate better with things like Hackett’s type language.</p><p>Finally, as seems to be the case more and more with my blog posts, I cannot express enough thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, without whose help I would probably not have been able to get everything working (not to mention that the Racket macro system would not exist without Matthew inventing and implementing it nearly singlehandedly). Matthew does an almost unfathomable number of things for Racket already without me pestering him with questions, bug reports, and feature requests, but he’s always patient and helpful all the same. Also, once again, I’d like to thank <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> for <a href="https://www2.ccs.neu.edu/racket/pubs/dissertation-culpepper.pdf">his incredible work on constructing tools for the working macro developer</a>, including writing the fantastic <code>syntax/parse</code> library that powers essentially everything I do. Thank you both.</p><ol class="footnotes"></ol></article>A space of their own: adding a type namespace to Hacketthttps://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/https://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/27 Oct 2017<article><p>As previously discussed on this blog, <a href="https://github.com/lexi-lambda/hackett">my programming language, Hackett</a>, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?</p><p>For now, at least, the answer is that Hackett will emulate Haskell: <strong>Hackett now has two namespaces</strong>. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.</p><h2><a name="why-two-namespaces"></a>Why two namespaces?</h2><p>Before delving into the mechanics of how multi-namespace Hackett is implemented, it’s important to understand what Hackett’s namespaces actually are and why they exist in the first place. Its host language, Racket, is a descendant of Scheme, a Lisp derivative that famously chose to only use a single namespace. This means everything—from values to functions to classes—lives in a single namespace in Racket.</p><p>This is in stark contrast to Common Lisp, which opts to divide bindings into many namespaces, most notably pushing functions into a separate namespace from other variables. You can see this difference most strikingly when applying higher-order functions. In Racket, Clojure, and Scheme, functions can be passed freely as values:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="ss">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="ss">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="ss">c</span><span class="p">)))</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>In Common Lisp and other languages with two namespaces, functions may still be passed as values, but the programmer must explicitly <em>annotate</em> when they wish to use a value from a different namespace:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">mapcar</span><span class="w"> </span><span class="nf">#&#39;</span><span class="nb">car</span><span class="w"> </span><span class="o">&#39;</span><span class="p">((</span><span class="mi">1</span><span class="w"> </span><span class="nv">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="w"> </span><span class="nv">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="w"> </span><span class="nv">c</span><span class="p">)))</span> +<span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>The Common Lisp <code>#'x</code> reader abbreviation is equivalent to <code>(function x)</code>, and <code>function</code> is a special form that references a value in the function namespace.</p><p>While this distinction is somewhat arbitrary, it is generally my belief that the Scheme approach was, indeed, the right one. Runtime values are values, whether they are numbers, strings, or functions, and they ought to all be treated as equal citizens. After all, if a programmer wishes to define their own function-like thing, they should not be forced to make their abstraction a second-class citizen merely because it is slightly different from the built-in notion of a function. Higher-order functional programming encourages treating functions as ordinary values, and an arbitrary stratification of the namespace is antithetical to that mental model.</p><p>However, Hackett is a little different from all of the aforementioned languages because Hackett has <em>types</em>. Types are rather different from runtime values because they do not exist at all at runtime. One cannot use a type where a value is expected, nor can one use a value where a type is expected, so this distinction is <em>always</em> syntactically unambiguous.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> Even if types and values live in separate namespaces, there is no need for a <code>type</code> form a la CL’s <code>function</code> because it can always be determined implicitly.</p><p>For this reason, it makes a great deal of sense for Hackett to have separate type and value namespaces, permitting declarations such as the following:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tuple</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span></code></pre><p>This defines a binding named <code>Tuple</code> at the type level, which is a <em>type constructor</em> of two arguments that produces a type of kind <code>*</code>,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> and another binding named <code>Tuple</code> at the value level, which is a <em>value constructor</em> of two arguments that produces a value of type <code>(Tuple a b)</code>.</p><p>But why do we want to overload names in this way, anyway? How hard would it really be to just name the value constructor <code>tuple</code> instead of <code>Tuple</code>? Well, it wouldn’t be hard at all, if it weren’t for the unpleasant ambiguity such a naming convention introduces when pattern-matching. Consider the following code snippet:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>This works fine. But what happens if the programmer decides to change the name of the <code>bar</code> value?</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Foo</span><span class="w"> </span><span class="n">qux</span><span class="w"> </span><span class="p">(</span><span class="n">baz</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">foo-&gt;integer</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Foo</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">bar</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="mi">0</span><span class="p">]</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">baz</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="n">y</span><span class="p">])</span></code></pre><p>Can you spot the bug? Disturbingly, this code <em>still compiles</em>! Even though <code>bar</code> is not a member of <code>Foo</code> anymore, it’s still a valid pattern, since names used as patterns match anything, just as the <code>y</code> pattern matches against any integer inside the <code>baz</code> constructor. If Hackett had a pattern redundancy checker, it could at least hopefully catch this mistake, but as things are, this could would silently compile and do the wrong thing: <code>(foo-&gt;integer (baz 42))</code> will still produce <code>0</code>, not <code>42</code>, since the first case always matches.</p><p>Haskell escapes this flaw by syntactically distinguishing between patterns and ordinary bindings by requiring all constructors start with an uppercase letter. This means that programmers often want to define data constructors and type constructors with the same name, such as the <code>Tuple</code> example above, which is illegal if a programming language only supports a single namespace.</p><p>Although Hackett now supports two namespaces, it does not currently enforce this naming convention, but it seems like an increasingly good idea. Separating the namespaces is the biggest hurdle needed to implement such a feature, and happily, it is now complete. The <code>Tuple</code> example from above is perfectly legal Hackett.</p><h2><a name="adding-namespaces-to-a-language"></a>Adding namespaces to a language</h2><p>Hopefully, we now agree that it would be nice if Hackett had two namespaces, but that doesn’t really get us any closer to being able to <em>implement</em> such a feature. At its core, Hackett is still a Racket language, and Racket’s binding structure has no notion of namespaces. How can it possibly support a language with more than one namespace?</p><p>Fortunately, Racket is no ordinary language—it is a language with a highly formalized notion of lexical scope, and many of its low-level scope control features are accessible to ordinary programmers. Before we get into the details, however, a forewarning: <strong>the remainder of this blog post is <em>highly technical</em>, and some of it involves some of the more esoteric corners of Racket’s macro system</strong>. This blog post is <em>not</em> representative of most macros written in Racket, nor is it at all necessary to understand these things to be a working Racket or Hackett macrologist. It is certainly not a tutorial on any of these concepts, so if you find it intimidating, there is no shame in skipping the rest of this post! If, however, you think you can handle it, or if you simply want to stare into the sun, by all means, read on.</p><h3><a name="namespaces-as-scopes"></a>Namespaces as scopes</h3><p>With that disclaimer out of the way, let’s begin. As of this writing, the current Racket macroexpander uses a scoping model known as <a href="https://www.cs.utah.edu/plt/scope-sets/"><em>sets of scopes</em></a>, which characterizes the binding structure of a program by annotating identifiers with sets of opaque markers known as “scopes”. The details of Racket’s macro system are well outside the scope of this blog post, but essentially, two identifiers with the same name can be made to refer to different bindings by adding a unique scope to each identifier.</p><p>Using this system of scopes, it is surprisingly simple to create a system of two namespaces: we only need to arrange for all identifiers in a value position to have a particular scope, which we will call the <em>value scope</em>, and all identifiers in type position must have a different scope, which we will call the <em>type scope</em>. How do we create these scopes and apply them to identifiers? In Racket, we use a function called <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-introducer%29%29"><code>make-syntax-introducer</code></a>, which produces a function that encapsulates a fresh scope. This function can be applied to any syntax object (Racket’s structured representation of code that includes lexical binding information) to do one of three things: it can <em>add</em> the scope to all pieces of the syntax object, <em>remove</em> the scope, or <em>flip</em> the scope (that is, add it to pieces of the syntax object that do not have it and remove it from pieces that do have it). In practice, this means we need to call <code>make-syntax-introducer</code> once for each namespace:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>We define these in a <code>begin-for-syntax</code> block because these definitions will be used in our compile-time macros (aka “phase 1”), not in runtime code (aka “phase 0”). Now, we can write some macros that use these introducer functions to apply the proper scopes to their contents:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Each of these two forms is like <code>begin</code>, which is a Racket form that is, for our purposes, essentially a no-op, but it applies <code>value-introducer</code> or <code>type-introducer</code> to add the appropriate scope. We can test that this works by writing a program that uses the two namespaces:</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">value-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">type-x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span> + +<span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">))</span></code></pre><p>This program produces the following output:</p><pre><code>'value-x +'type-x +</code></pre><p>It works! Normally, if you try to define two bindings with the same name in Racket, it will produce a compile-time error, but by assigning them different scopes, we have essentially managed to create two separate namespaces.</p><p>However, although this is close, it isn’t <em>quite</em> right. What happens if we nest the two inside each other?</p><pre><code class="pygments"><span class="p">(</span><span class="n">begin/value</span> +<span class="w"> </span><span class="p">(</span><span class="n">begin/type</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span></code></pre><pre><code>x: identifier's binding is ambiguous + context...: + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + #(189465 use-site) #(190351 use-site) #(190354 use-site) #(190358 local) + #(190359 intdef) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189464 use-site) + matching binding...: + #&lt;module-path-index:()&gt; + #(189267 module) #(189268 module anonymous-module 0) #(189465 use-site) +</code></pre><p>Oh no! That didn’t work at all. The error is a bit of a scary one, but the top of the error message is essentially accurate: the use of <code>x</code> is <em>ambiguous</em> because it has both scopes on it, so it could refer to either binding. What we really want is for nested uses of <code>begin/value</code> or <code>begin/type</code> to <em>override</em> outer ones, ensuring that a use can only be in a single namespace at a time.</p><p>To do this, we simply need to adjust <code>begin/value</code> and <code>begin/type</code> to remove the other scope in addition to adding the appropriate one:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/value</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">begin/type</span><span class="w"> </span><span class="n">form</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">stx</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">add</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">remove</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">form</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin</span><span class="w"> </span><span class="n">form*</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>Now our nested program runs, and it produces <code>'type-x</code>, which is exactly what we want—the “nearest” scope wins.</p><p>With just a few lines of code, we’ve managed to implement the two-namespace system Hackett needs: we simply maintain two scopes, one for each namespace, and arrange for all the types to have the type scope applied and everything else to have the value scope applied. Easy, right? Well, not quite. Things start to get a lot more complicated once our programs span more than a single module.</p><h3><a name="namespaces-that-cross-module-boundaries"></a>Namespaces that cross module boundaries</h3><p>The system of using two syntax introducers to manage scopes is wonderfully simple as long as all of our programs are contained within a single module, but obviously, that is never true in practice. It is critical that users are able to export both values and types from one module and import them into another, as that is a pretty fundamental feature of any language. This is, unfortunately, where we start to run into problems.</p><p>Racket’s notion of hygiene is pervasive, but it is still essentially scoped to a single module. This makes sense, since each module conceptually has its own “module scope”, and it wouldn’t be very helpful to inject a binding from a different module with the <em>other</em> module’s scope—it would be impossible to reference the binding in the importing module. Instead, Racket’s modules essentially export <em>symbols</em>, not identifiers (which, in Racket terminology, are symbols packaged together with their lexical scope). When a Racket module provides a binding named <code>foo</code>, there is no other information attached to that binding. It does not have any scopes attached to it, since it is the <code>require</code> form’s job to attach the correct scopes to imported identifiers.</p><p>This completely makes sense for all normal uses of the Racket binding system, but it has unfortunate implications for our namespace system: Racket modules cannot export more than one binding with a given symbolic name!<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup> This won’t work at all, since a Hackett programmer might very well want to export a type and value with the same name from a single module. Indeed, this capability is one of the primary <em>points</em> of having multiple namespaces.</p><p>What to do? Sadly, Racket does not have nearly as elegant a solution for this problem, at least not at the time of this writing. Fortunately, hope is not lost. While far from perfect, we can get away with a relatively simple name-mangling scheme to prefix types upon export and unprefix them upon import. Since Racket’s <code>require</code> and <code>provide</code> forms are extensible, it’s even possible to implement this mangling in a completely invisible way.</p><p>Currently, the scheme that Hackett uses is to prefix <code>#%hackett-type:</code> onto the beginning of any type exports. This can be defined in terms of a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._provide._pre._transformer%29"><em>provide pre-transformer</em></a>, which is essentially a macro that cooperates with Racket’s <code>provide</code> form to control the export process. In this case, we can define our <code>type-out</code> provide pre-transformer in terms of <a href="https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prefix-out%29%29"><code>prefix-out</code></a>, a form built-in to Racket that allows prefixing the names of exports:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">type-out</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-provide-pre-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">stx</span><span class="w"> </span><span class="n">modes</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">pre-expand-export</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="k">prefix-out</span><span class="w"> </span><span class="n">#%hackett-type:</span> +<span class="w"> </span><span class="o">#,</span><span class="p">(</span><span class="n">type-introducer</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-out</span><span class="w"> </span><span class="n">provide-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span> +<span class="w"> </span><span class="n">modes</span><span class="p">)]))))</span></code></pre><p>Note that we call <code>type-introducer</code> in this macro! That’s because we want to ensure that, when a user writes <code>(provide (type-out Foo))</code>, we look for <code>Foo</code> in the module’s type namespace. Of course, once it is provided, all that scoping information is thrown away, but we still need it around so that <code>provide</code> knows <em>which</em> <code>Foo</code> is being provided.</p><p>Once we have referenced the correct binding, the use of <code>prefix-out</code> will appropriately add the <code>#%hackett-type:</code> prefix, so the exporting side is already done. Users do need to explicitly write <code>(type-out ....)</code> if they are exporting a particular type-level binding, but this is rarely necessary, since most users use <code>data</code> or <code>class</code> to export datatypes or typeclasses respectively, which can be modified to use <code>type-out</code> internally. Very little user code actually needs to change to support this adjustment.</p><p>Handling imports is, comparatively, tricky. When exporting, we can just force the user to annotate which exports are types, but we don’t have that luxury when importing, since it is merely whether or not a binding has the <code>#%hackett-type:</code> prefix that indicates which namespace it should be imported into. This means we’ll need to explicitly iterate through every imported binding and check if it has the prefix or not. If it does, we need to strip it off and add the type namespace; otherwise, we just pass it through unchanged.</p><p>Just as we extended <code>provide</code> with a provide pre-transformer, we can extend <code>require</code> using a <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28tech._require._transformer%29"><em>require transformer</em></a>. In code, this entire process looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">name</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">and~&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">regexp-match</span><span class="w"> </span><span class="sr">#rx"^#%hackett-type:(.+)$"</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="nb">second</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">unmangle-types-in</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-require-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:do</span><span class="w"> </span><span class="p">[(</span><span class="k">define-values</span><span class="w"> </span><span class="p">[</span><span class="n">imports</span><span class="w"> </span><span class="n">sources</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">expand-import</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">combine-in</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">values</span> +<span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="k">match-lambda</span> +<span class="w"> </span><span class="p">[(</span><span class="k">and</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="n">local-id</span><span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">local-name</span><span class="w"> </span><span class="p">(</span><span class="nb">symbol-&gt;string</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-e</span><span class="w"> </span><span class="n">local-id</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">unmangled-type-name</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-type-name</span><span class="w"> </span><span class="n">local-name</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="n">unmangled-type-name</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">unmangled-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;symbol</span><span class="w"> </span><span class="n">unmangled-type-name</span><span class="p">)</span> +<span class="w"> </span><span class="n">local-id</span> +<span class="w"> </span><span class="n">local-id</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">import</span><span class="w"> </span><span class="p">(</span><span class="n">type-introducer</span><span class="w"> </span><span class="n">unmangled-id</span><span class="p">)</span> +<span class="w"> </span><span class="n">src-sym</span><span class="w"> </span><span class="n">src-mod-path</span><span class="w"> </span><span class="n">mode</span><span class="w"> </span><span class="n">req-mode</span><span class="w"> </span><span class="n">orig-mode</span><span class="w"> </span><span class="n">orig-stx</span><span class="p">))</span> +<span class="w"> </span><span class="n">i</span><span class="p">))])</span> +<span class="w"> </span><span class="n">imports</span><span class="p">)</span> +<span class="w"> </span><span class="n">sources</span><span class="p">)])))</span></code></pre><p>This is a little intimidating if you are not familiar with the intricacies of Racket’s low-level macro system, but the bulk of the code isn’t as scary as it may seem. It essentially does three things:</p><ol><li><p>It iterates over each import and calls <code>unmangle-type-name</code> on the imported symbol. If the result is <code>#f</code>, that means the import does not have the <code>#%hackett-type:</code> prefix, and it can be safely passed through unchanged.</p></li><li><p>If <code>unmangle-type-name</code> does <em>not</em> return <code>#f</code>, then it returns the unprefixed name, which is then provided to <code>datum-&gt;syntax</code>, which allows users to forge new identifiers in an <em>unhygienic</em> (or “hygiene-bending”) way. In this case, we want to forge a new identifier with the name we get back from <code>unmangle-type-name</code>, but with the lexical context of the original identifier.</p></li><li><p>Finally, we pass the new identifier to <code>type-introducer</code> to properly add the type scope, injecting the fresh binding into the type namespace.</p></li></ol><p>With this in place, we now have a way for Hackett users to import and export type bindings, but while it is not much of a burden to write <code>type-out</code> when exporting types, it is unlikely that users will want to write <code>unmangle-types-in</code> around each and every import in their program. For that reason, we can define a slightly modified version of <code>require</code> that implicitly wraps all of its subforms with <code>unmangle-types-in</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="p">(</span><span class="k">rename-out</span><span class="w"> </span><span class="p">[</span><span class="n">require/unmangle</span><span class="w"> </span><span class="k">require</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">require/unmangle</span><span class="w"> </span><span class="n">require-spec</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="n">unmangle-types-in</span><span class="w"> </span><span class="n">require-spec</span><span class="p">)</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>…and we’re done. Now, Hackett modules can properly import and export type-level bindings.</p><h3><a name="namespaces-plus-submodules-the-devil-s-in-the-details"></a>Namespaces plus submodules: the devil’s in the details</h3><p>Up until this point, adding namespaces has required some understanding of the nuances of Racket’s macro system, but it hasn’t been particularly difficult to implement. However, getting namespaces right is a bit trickier than it appears. One area where namespaces are less than straightforward is Racket’s system of <em>submodules</em>.</p><p>Submodules are a Racket feature that allows the programmer to arbitrarily nest modules. Each file always corresponds to a single outer module, but that module can contain an arbitrary number of submodules. Each submodule can have its own “module language”, which even allows different languages to be mixed within a single file.</p><p>Submodules in Racket come in two flavors: <code>module</code> and <code>module*</code>. The difference is what order, semantically, they are defined in. Submodules defined with <code>module</code> are essentially defined <em>before</em> their enclosing module, so they cannot import their enclosing module, but their enclosing module can import them. Modules defined with <code>module*</code> are the logical dual to this: they are defined after their enclosing module, so they can import their enclosing module, but the enclosing module cannot import them.</p><p>How do submodules interact with namespaces? Well, for the most part, they work totally fine. This is because submodules are really, for the most part, treated like any other module, so the same machinery that works for ordinary Racket modules works fine with submodules.</p><p>However, there is <a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._submodules%29">a special sort of <code>module*</code> submodule that uses <code>#f</code> in place of a module language</a>, which gives a module access to <em>all</em> of its enclosing module’s bindings, even ones that aren’t exported! This is commonly used to create a <code>test</code> submodule that contains unit tests, and functions can be tested in such a submodule even if they are not part of the enclosing module’s public API:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="c1">; not provided</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="mi">1</span><span class="p">))</span> + +<span class="p">(</span><span class="k">module*</span><span class="w"> </span><span class="n">test</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">rackunit</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">check-equal?</span><span class="w"> </span><span class="p">(</span><span class="n">private-add1</span><span class="w"> </span><span class="mi">41</span><span class="p">)</span><span class="w"> </span><span class="mi">42</span><span class="p">))</span></code></pre><p>It would be nice to be able to use these sorts of submodules in Hackett, too, but if we try, we’ll find that types from the enclosing module mysteriously can’t be referenced by the submodule. Why? Well, the issue is in how we naïvely create our type and value introducers:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span><span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-introducer</span><span class="p">)))</span></code></pre><p>Remember that <code>make-syntax-introducer</code> is generative—each time it is called, it produces a function that operates on a fresh scope. This is a problem, since those functions will be re-evaluated on every module <a href="https://docs.racket-lang.org/reference/eval-model.html#%28tech._instantiate%29">instantiation</a>, as ensured by Racket’s <a href="https://docs.racket-lang.org/reference/eval-model.html#%28part._separate-compilation%29">separate compilation guarantee</a>. This means that each module gets its <em>own</em> pair of scopes. This means the body of a <code>module*</code> submodule will have different scopes from its enclosing module, and the enclosing modules bindings will not be accessible.</p><p>Fortunately, there is a way to circumvent this. While we cannot directly preserve syntax introducers across module instantiations, we <em>can</em> preserve syntax objects by embedding them in the expanded program, and we can attach scopes to syntax objects. Using <a href="https://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._make-syntax-delta-introducer%29%29"><code>make-syntax-delta-introducer</code></a>, we can create a syntax introducer the adds or removes the <em>difference</em> between scopes on two syntax objects. Pairing this with a little bit of clever indirection, we can arrange for <code>value-introducer</code> and <code>type-introducer</code> to always operate on the same scopes on each module instantiation:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-value/type-introducers</span> +<span class="w"> </span><span class="n">value-introducer:id</span><span class="w"> </span><span class="n">type-introducer:id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">scopeless-id</span><span class="w"> </span><span class="p">(</span><span class="nb">datum-&gt;syntax</span><span class="w"> </span><span class="no">#f</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">introducer-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">value-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="n">type-id</span><span class="w"> </span><span class="p">((</span><span class="nb">make-syntax-introducer</span><span class="p">)</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">value-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">type-introducer</span> +<span class="w"> </span><span class="p">(</span><span class="nb">make-syntax-delta-introducer</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">type-id</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">scopeless-id</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-value/type-introducers</span><span class="w"> </span><span class="n">value-introducer</span><span class="w"> </span><span class="n">type-introducer</span><span class="p">)</span></code></pre><p>The way this trick works is subtle, but to understand it, it’s important to understand that when a module is compiled, its macro uses are only evaluated once. Subsequent imports of the same module will not re-expand the module. <em>However</em>, code inside <code>begin-for-syntax</code> blocks is still re-evaluated every time the module is instantiated! This means we are <em>not</em> circumventing that re-evaluation directly, we are merely arranging for each re-evaluation to always produce the same result.</p><p>We still use <code>make-syntax-introducer</code> to create our two scopes, but critically, we only call <code>make-syntax-introducer</code> inside the <code>define-value/type-introducers</code> macro, which is, again, only run once (when the module is expanded). The resulting compiled module embeds <code>value-id</code> and <code>type-id</code> as syntax objects in the fully-expanded program, so they never change on each module instantiation, and they already contain the appropriate scopes. We can use <code>make-syntax-delta-introducer</code> to convert the “inert” scopes into introducer functions that we can use to apply the scopes to other syntax objects as we see fit.</p><p>By guaranteeing each namespace’s scope is always the same, even for different modules, <code>module*</code> submodules now work properly, and they are able to refer to bindings inherited from their enclosing module as desired.</p><h3><a name="the-final-stretch-making-scribble-documentation-namespace-aware"></a>The final stretch: making Scribble documentation namespace-aware</h3><p>As discussed in <a href="/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/">my previous blog post</a>, Hackett has comprehensive documentation powered by Racket’s excellent documentation tool, Scribble. Fortunately for Hackett, Scribble is incredibly flexible, and it can absolutely cope with a language with multiple namespaces. Less fortunately, it is clear that Scribble’s built-in documentation forms were not at all designed with multiple namespaces in mind.</p><p>In general, documenting such a language is tricky, assuming one wishes all identifiers to be properly hyperlinked to their appropriate definition (which, of course, I do). However, documentation is far more ambiguous than code when attempting to determine which identifiers belong in which namespace. When actually writing Hackett code, forms can always syntactically deduce the appropriate namespace for their subforms and annotate them accordingly, but this is not true in documentation. Indeed, it’s entirely possible that a piece of documentation might include intentionally incorrect code, which cannot be expanded at all!</p><p>Haskell’s documentation tool, Haddock, does not appear to attempt to tackle this problem at all—when given an identifier that exists in both namespaces, it will generate a hyperlink to the type, not the value. I do not know if there is a way around this, but if there is, it isn’t documented. This works alright for Haddock because Haskell’s documentation generally contains fewer examples, and Haskell programmers do not expect all examples to be appropriately hyperlinked, so a best-effort approach is accepted. Racket programmers, however, are used to a very high standard of documentation, and incorrectly hyperlinked docs are unacceptable.</p><p>To work around this problem, Hackett’s documentation requires that users explicitly annotate which identifiers belong to the type namespace. Identifiers in the type namespace are prefixed with <code>t:</code> upon import, and they are bound to Scribble <a href="https://docs.racket-lang.org/scribble/scheme.html#%28tech._element._transformer%29"><em>element transformers</em></a> that indicate they should be typeset without the <code>t:</code> prefix. Fortunately, Scribble’s documentation forms <em>do</em> understand Racket’s model of lexical scope (mostly), so they can properly distinguish between two identifiers with the same name but different lexical context.</p><p>In practice, this means Hackett documentation must now include a proliferation of <code>t:</code> prefixes. For example, here is the code for a typeset REPL interaction:</p><pre><code class="pygments"><span class="n">@</span><span class="p">(</span><span class="n">hackett-examples</span> +<span class="w"> </span><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">square</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">t:-&gt;</span><span class="w"> </span><span class="n">t:Integer</span><span class="w"> </span><span class="n">t:Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">[[</span><span class="n">x</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">x</span><span class="p">}])</span> +<span class="w"> </span><span class="p">(</span><span class="n">square</span><span class="w"> </span><span class="mi">5</span><span class="p">))</span></code></pre><p>Note the use of <code>t:-&gt;</code> and <code>t:Integer</code> instead of <code>-&gt;</code> and <code>Integer</code>. When the documentation is rendered and the example is evaluated, the prefixes are stripped, resulting in properly-typeset Hackett code.</p><p>This also means Hackett’s documentation forms have been updated to understand multiple namespaces. Hackett now provides <code>deftype</code> and <code>deftycon</code> forms for documenting types and type constructors, respectively, which will use the additional lexical information attached to <code>t:</code>-prefixed identifiers to properly index documented forms. Similarly, <code>defdata</code> and <code>defclass</code> have been updated with an understanding of types.</p><p>The implementation details of these changes is less interesting than the ones made to the code itself, since it mostly just involved tweaking Racket’s implementation of <code>defform</code> slightly to cooperate with the prefixed identifiers. To summarize, Hackett defines a notion of “type binding transformers” that include information about both prefixed and unprefixed versions of types, and Hackett provides documentation forms that consume that information when typesetting. A require transformer converts imported bindings into <code>t:</code>-prefixed ones and attaches the necessary compile-time information to them. It isn’t especially elegant, but it works.</p><h2><a name="analysis-and-unsolved-problems"></a>Analysis and unsolved problems</h2><p>When laid out from top to bottom in this blog post, the amount of code it takes to actually implement multiple namespaces in Racket is surprisingly small. In hindsight, it does not feel like two weeks worth of effort, but it would be disingenuous to suggest that any of this was obvious. I tried a variety of different implementation strategies and spent a great deal of time staring at opaque error messages and begging <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for help before I got things working properly. Fortunately, with everything in place, the implementation seems reliable, predictable, and useful for Hackett’s users (or, as the case may be, users-to-be).</p><p>For the most part, all the machinery behind multiple namespaces is invisible to the average Hackett programmer, and it seems to “just work”. For completeness, however, I must mention one unfortunate exception: remember the work needed to unmangle type names? While it’s true that all imports into Hackett modules are automatically unmangled by the custom <code>require</code> form, types provided by a module’s <em>language</em> are not automatically unmangled. This is because Racket does not currently provide a hook to customize how bindings from a module language are introduced, unlike <code>require</code>’s require transformers.</p><p>To circumvent this restriction, <code>#lang hackett</code>’s reader includes a somewhat ad-hoc solution that actually inserts a <code>require</code> into users’ programs that unmangles and imports all the types provided by the module. This mostly works, but due to the way Racket’s imports work, it isn’t possible for Racket programmers to import different types with the same names as Hackett core types; the two bindings will conflict, and there is no way for users to hide these implicitly imported bindings. Whether or not this is actually a common problem remains to be seen. If it is rare, it might be sufficient to introduce an ad-hoc mechanism to hide certain type imports, but it might be better to extend Racket in some way to better support this use-case.</p><p>That issue aside, multi-namespace Hackett is now working smoothly. It’s worth nothing that I did not have to do <em>any</em> special work to help Racket’s tooling, such as DrRacket’s Check Syntax tool, understand the binding structure of Hackett programs. Since other tools, such as racket-mode for Emacs, use the same mechanisms under the hood, Racket programmers’ existing tools will be able to properly locate the distinct definition sites for types and values with the same name, another example of how Racket successfully <a href="http://www.ccs.neu.edu/home/matthias/manifesto/sec_intern.html">internalizes extra-linguistic mechanisms</a>.</p><p>As closing notes, even if the majority of this blog post was gibberish to you, do note that Hackett has come quite a long way in just the past two months, adding much more than just a separate type namespace. I might try and give a more comprehensive update at a later date, but here’s a quick summary of the meaningful changes for those interested:</p><ul><li><p><strong>Multi-parameter typeclasses</strong> are implemented, along with <strong>default typeclass method implementations</strong>.</p></li><li><p>Pattern-matching performs basic <strong>exhaustiveness checking</strong>, so unmatched cases are a compile-time error.</p></li><li><p>Hackett ships with a <strong>larger standard library</strong>, including an <code>Either</code> type and appropriate functions, an <code>Identity</code> type, a <code>MonadTrans</code> typeclass, and the <code>ReaderT</code> and <code>ErrorT</code> monad transformers.</p></li><li><p><strong>More things are documented</strong>, and parts of the documentation are slightly improved. Additionally, <strong>Hackett’s internals are much more heavily commented</strong>, hopefully making the project more accessible to new contributors.</p></li><li><p><strong>Parts of the typechecker are dramatically simplified</strong>, improving the mechanisms behind dictionary elaboration and clearing the way for a variety of additional long-term improvements, including multiple compilation targets and a type-aware optimizer.</p></li><li><p>As always, various bug fixes.</p></li></ul><p>Finally, special mention to two new contributors to Hackett, <a href="https://github.com/iitalics">Milo Turner</a> and <a href="https://github.com/Shamrock-Frost">Brendan Murphy</a>. Also special thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> and <a href="https://github.com/michaelballantyne">Michael Ballantyne</a> for helping me overcome two of the trickiest macro-related problems I’ve encountered in Hackett to date. It has now been just over a year since Hackett’s original conception and roughly six months since the first commit of its current implementation, and the speed at which I’ve been able to work would not have been possible without the valuable help of the wonderful Racket community. Here’s hoping this is only the beginning.</p><ol class="footnotes"><li id="footnote-1"><p>“But what about dependent types?” you may ask. Put simply, Hackett is not dependently typed, and it is not going to be dependently typed. Dependent types are currently being bolted onto Haskell, but Haskell does not have <code>#lang</code>. Racket does. It seems likely that a dependently-typed language would be much more useful as a separate <code>#lang</code>, not a modified version of Hackett, so Hackett can optimize its user experience for what it <em>is</em>, not what it might be someday. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Hackett does not actually have a real kind system yet, but pleasantly, this same change will allow <code>*</code> to be used to mean “type” at the kind level and “multiply” at the value level. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>This isn’t strictly true, as readers familiar with Racket’s macro system may likely be aware that Racket modules export bindings at different “phase levels”, where phase levels above 0 correspond to compile-time macroexpansion phases. Racket modules are allowed to export a single binding per name, <em>per phase</em>, so the same symbolic name can be bound to different things at different phases. This isn’t meaningfully relevant for Hackett, however, since types and values are both exported at phase 0, and there are reasons that must be the case, this phase separation does not make this problem any simpler. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Hackett progress report: documentation, quality of life, and snakehttps://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/https://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/28 Aug 2017<article><p>Three months ago, <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">I wrote a blog post describing my new, prototype implementation of my programming language, Hackett</a>. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/"><strong>the first (albeit quite incomplete) approach to Hackett’s documentation</strong></a>.</p><p>I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.</p><h2><a name="a-philosophy-of-documentation"></a>A philosophy of documentation</h2><p>Racket, as a project, has always had <a href="http://docs.racket-lang.org">wonderful documentation</a>. There are many reasons for this—Racket’s educational origins almost certainly play a part, and it helps that the core packages set the bar high—but one of the biggest reasons is undoubtably <a href="http://docs.racket-lang.org/scribble/index.html">Scribble, the Racket documentation tool</a>. Scribble is, in many ways, the embodiment of the Racket philosophy: it is a user-extensible, fully-featured, domain-specific programming language designed for typesetting, with <a href="http://docs.racket-lang.org/scribble/plt-manuals.html">a powerful library for documenting Racket code</a>. Like the Racket language itself, Scribble comes with a hygienic macro system, and in fact, all Racket libraries are trivially usable from within Scribble documents, if desired. The macro system is used to great effect to provide typesetting forms tailored to the various sorts of things a Racket programmer might wish to document, such as procedures, structures, and macros.</p><p>Scribble documents are decoupled from a rendering backend, so a single Scribble document can be rendered to plain text, a PDF, or HTML, but the HTML backend is the most useful for writing docs. Scribble documents themselves use a syntax inspired by (La)TeX’s syntax, but Scribble uses an <code>@</code> character instead of <code>\</code>. It also generalizes and regularizes TeX in many ways, creating a much more uniform language without nearly so much magic or complexity. Since Scribble’s “at-expressions” are merely an alternate syntax for Racket’s more traditional s-expressions, Scribble documents can be built out of ordinary Racket macros. For example, to document a procedure in Racket, one would use <a href="http://docs.racket-lang.org/scribble/doc-forms.html#%28form._%28%28lib._scribble%2Fmanual..rkt%29._defproc%29%29">the provided <code>defproc</code> form</a>:</p><pre><code class="pygments"><span class="n">@defproc</span><span class="p">[(</span><span class="nb">add1</span><span class="w"> </span><span class="p">[</span><span class="n">z</span><span class="w"> </span><span class="nb">number?</span><span class="p">])</span><span class="w"> </span><span class="nb">number?</span><span class="p">]{</span> +<span class="n">Returns</span><span class="w"> </span><span class="n">@racket</span><span class="p">[(</span><span class="nb">+</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="mi">1</span><span class="p">)]</span><span class="o">.</span><span class="p">}</span></code></pre><p>This syntax may look alien to someone more familiar with traditional, Javadoc-style documentation comments, but the results are quite impressive. The above snippet renders into <a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29">something like this</a>:</p><p><a href="http://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._add1%29%29"></a></p><p>The fact that Scribble documents are fully-fledged <em>programs</em> equips the programmer with a lot of power. One of the most remarkable tools Scribble provides is <a href="http://docs.racket-lang.org/scribble/eval.html">the <code>scribble/example</code> module</a>, a library that performs sandboxed evaluation as part of the rendering process. This allows Scribble documents to include REPL-style examples inline, automatically generated as part of typesetting, always kept up to date from a single source of truth: the implementation. It even provides a special <code>eval:check</code> form that enables <a href="https://docs.python.org/3/library/doctest.html">doctest</a>-like checking, which allows documentation to serve double duty as a test suite.</p><p>Of course, Hackett is not Racket, though it shares many similarities. Fortunately, all of Racket is <em>designed</em> with the goal of supporting many different programming languages, and Scribble is no exception. Things like <a href="http://docs.racket-lang.org/scribble/eval.html"><code>scribble/example</code></a> essentially work out of the box with Hackett, and most of <a href="http://docs.racket-lang.org/scribble/plt-manuals.html"><code>scribble/manual</code></a> can be reused. However, what about documenting algebraic datatypes? What about documenting typeclasses? Well, remember: Scribble is extensible. The <code>defproc</code> and <code>defstruct</code> forms are hardly builtins; they are defined as part of the <code>scribble/manual</code> library in terms of Scribble primitives, and <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-doc/scribble/manual/hackett.rkt">we can do the same</a>.</p><p>Hackett’s documentation already defines three new forms, <code>defdata</code>, <code>defclass</code>, and <code>defmethod</code>, for documenting algebraic datatypes, typeclasses, and typeclass methods, respectively. They typeset documentation custom-tailored to Hackett’s needs, so Hackett’s documentation need not be constrained by Racket’s design decisions. For example, one could document the <code>Functor</code> typeclass using <code>defclass</code> like this:</p><pre><code class="pygments"><span class="n">@defclass</span><span class="p">[(</span><span class="n">Functor</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="nb">map</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="p">)})]]{</span> + +<span class="n">A</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">are</span><span class="w"> </span><span class="n">@deftech</span><span class="p">{</span><span class="n">functors</span><span class="p">}</span><span class="o">,</span><span class="w"> </span><span class="n">essentially</span><span class="w"> </span><span class="n">types</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="k">provide</span><span class="w"> </span><span class="n">a</span> +<span class="n">mapping</span><span class="w"> </span><span class="k">or</span><span class="w"> </span><span class="n">“piercing”</span><span class="w"> </span><span class="n">operation.</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">@racket</span><span class="p">[</span><span class="nb">map</span><span class="p">]</span><span class="w"> </span><span class="n">function</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">viewed</span><span class="w"> </span><span class="n">in</span> +<span class="n">different</span><span class="w"> </span><span class="n">ways:</span> + +<span class="k">...</span><span class="p">}</span></code></pre><p>With only a little more than the above code, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29">Hackett’s documentation includes a beautifully-typeset definition of the <code>Functor</code> typeclass</a>, including examples and rich prose:</p><p><a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Functor%29%29"></a></p><p>Scribble makes Hackett’s documentation shine.</p><h3><a name="a-tale-of-two-users"></a>A tale of two users</h3><p>For a programming language, documentation is critical. Once we have grown comfortable with a language, it’s easy to take for granted our ability to work within it, but there is always a learning period, no matter how simple or familiar the language may be. When learning a new language, we often relate the languages’ concepts and features to those which we already know, which is why having a broad vocabulary of languages makes picking up new ones so much easier.</p><p>A new user of a language needs a gentle introduction to its features, structured in a logical way, encouraging this period of discovery and internalization. Such an introduction should come equipped with plenty of examples, and it shouldn’t worry itself with being an authoritative reference. Some innocent simplifications are often conducive to learning, and it is unlikely to be helpful to force the full power of a language onto a user all at once.</p><p>However, for experienced users, an authoritative reference is <em>exactly</em> what they need. While learners want tutorial-style documentation that encourages experimentation and exploration, working users of a language need something closer to a dictionary or encyclopedia: a way to look up forms and functions by name and find precise definitions, complete explanations, and hopefully a couple of examples. Such a user does not want information to be scattered across multiple chapters of explanatory text; they simply need a focused, targeted, one-stop shop for the information they’re looking for.</p><p>This dichotomy is rarely well-served by existing programming language documentation. Most programming languages suffer from either failing entirely to serve both types of users, or doing so in a way that enforces too strong a separation between the styles of documentation. For example:</p><ul><li><p>Java ships with a quintessential example of a documentation generator: Javadoc. Java is a good case study because, although its documentation is not particularly good, it still manages to be considerably better than most languages’ docs.</p><p><a href="https://docs.oracle.com/javase/8/docs/api/">Java’s API documentation</a> documents its standard library, but it doesn’t document the language. Reference-style language documentation is largely relegated to the Java Language Specification, which is highly technical and rather low-level. It is more readable than the standards for some other languages, but it’s still mostly only useful to language lawyers. For Java, this ends up being mostly okay, largely because Java is a fairly <em>small</em> language that does not often change.</p><p>On the other hand, Java’s reference documentation is inconsistent, rarely provides any examples, and certainly does not do a good job of serving new users. Java <em>does</em> provide guide-style documentation in the form of the <a href="https://docs.oracle.com/javase/tutorial/">Java Tutorials</a>, but they are of inconsistent quality.</p><p>More importantly, while the Java tutorials link to the API docs, the reverse is <strong>not</strong> true, which is a real disservice. One of the most beautiful things about the web is how information can be extensively cross-linked, and exploring links is many times easier than turning pages of a physical book. Anyone who’s explored topics on Wikipedia for an hour (or more) at a time knows how amazing this can be.</p><p>Language documentation isn’t quite the same as an encyclopedia, but it’s a shame that Java’s documentation does not lend itself as easily to curious, open-ended learning. If the API docs frequently linked to relevant portions of the tutorials, then a user could open the Javadoc for a class or method they are using, then quickly jump to the relevant guide. As the documentation is currently organized, this is nearly impossible, and tutorials are only discovered when explicitly looking for them.</p></li><li><p>Other languages, such as JavaScript, are in even worse boats than Java when it comes to documentation. For whatever reason, structured documentation of any kind doesn’t seem to have caught on in the JavaScript world, probably largely because no documentation tool ships with the language, and no such tool ever became standard. Whatever the reason, JavaScript libraries’ documentation largely resides in markdown documents spread across version control repositories and various webpages.</p><p>The closest thing that JavaScript has to official language documentation, aside from the (largely incomprehensible) language standard, is <a href="https://developer.mozilla.org/en-US/">MDN</a>. MDN’s docs are actually quite good, and they tend to mix lots of examples together with reference-style documentation. They’re indexed and searchable, and they have a great Google search ranking. MDN is easily my go-to place to read about core JavaScript functions.</p><p>The trouble, of course, is that MDN only houses documentation for the standard library, and while new standards make it bigger than ever, huge amounts of critical functionality are often offloaded to separate packages. These libraries all have their own standards and styles of documentation, and virtually none of them even compare to MDN.</p><p>This means that documentation for JavaScript libraries, even the most popular ones, tends to be all over the map. <a href="http://ramdajs.com/docs/">Ramda’s documentation is nothing but a reference</a>, which makes it easy to look up information about a specific function, but nearly impossible to find anything if you don’t have a specific name to look for. In contrast, <a href="http://passportjs.org/docs">Passport’s docs are essentially <em>only</em> a set of tutorials</a>, which is great for learners, but enormously frustrating if I just want to look up what the heck a specific function or method <em>does</em>. Fortunately, <a href="https://facebook.github.io/react/docs/hello-world.html">there are some libraries, like React</a>, that absolutely <em>nail</em> this, and they have both styles of documentation that are <strong>actually cross-referenced</strong>. Unfortunately, those are mostly the exceptions, not the norm.</p></li><li><p><a href="https://docs.python.org/3/index.html">Python’s documentation is interesting</a>, since it includes a set of tutorials alongside the API reference, and it <em>also</em> ships a language reference written for ordinary users. In many ways, it does everything right, but disappointingly, it generally doesn’t link back to the tutorials from the API docs, even though the reverse is true. For example, the section in the tutorial on <code>if</code> links to the section in the reference about <code>if</code>, but nothing goes in the other direction, which is something of a missed opportunity.</p></li><li><p><a href="https://hackage.haskell.org/package/base">Haskell manages to be especially bad here</a> (maybe even notoriously bad) despite having an ubiquitous documentation generator, Haddock. Unfortunately, Haddock’s format makes writing prose and examples somewhat unpleasant, and very few packages provide any sort of tutorial. For those that do, the tutorial is often not included in the API docs, a common theme at this point.</p><p>It’s generally a bad sign when your documentation tool isn’t even powerful enough to document itself, and <a href="https://www.haskell.org/haddock/">Haddock’s docs are pretty impressively bad, though mostly serviceable if you’re willing to look</a>.</p></li></ul><p>The takeaway here is that I just don’t think most languages’ documentation is particularly good, and programmers seem to have gotten so used to this state of affairs that the bar is set disappointingly low. Fortunately, this is another area where Racket delivers. Racket, like Python, ships with <em>two</em> pieces of documentation: the <a href="http://docs.racket-lang.org/guide/index.html">Racket Guide</a> and the <a href="http://docs.racket-lang.org/reference/index.html">Racket Reference</a>. The guide includes over <strong>one hundred thousand</strong> words of explanations and examples, and the reference includes roughly <strong>half a million</strong>. Racket’s documentation is impressive on its own, but what’s equally impressive is how carefully and methodically cross-linked it is. Margin notes often provide links to corresponding sections in the relevant companion manual, so it’s easy to look up a form or function by name, then quickly jump to the section of the guide explaining it.</p><p>Hackett is obviously not going to have hundreds of thousands of words worth of documentation in its first few months of existence, but it already has nearly ten thousand, and that’s not nothing. More importantly, it is structured the same way that Racket’s docs are: it’s split into the <a href="http://docs.racket-lang.org/hackett/guide.html">Hackett Guide</a> and the <a href="http://docs.racket-lang.org/hackett/reference.html">Hackett Reference</a>, and the two are cross-referenced as much as possible. Haskell is a notoriously difficult language to learn, but my hope is that does not necessarily <em>need</em> to be the case. Documentation cannot make the language trivial, but my hope is that it can make it a <em>lot</em> more accessible without making it any less useful for power users.</p><h2><a name="rounding-hackett-s-library-sanding-its-edges"></a>Rounding Hackett’s library, sanding its edges</h2><p>One of the best things about sitting down and writing documentation—whether it’s for a tool, a library, or a language—is how it forces you, the author, to think about how someone else might perceive the project when seeing it for the first time. This encompasses everything: error messages, ease of installation, completeness of a standard library, friendliness of tooling, etc. Writing Hackett’s documentation forced me to make a <em>lot</em> of improvements, and while very few of them are flashy features, they make Hackett feel much less like a toy and more like a tool.</p><p>Hackett currently has no formal changelog because it is considered alpha quality, and its API is still unstable. There is no guarantee that things won’t change at any moment. Still, it’s useful to put together an ad-hoc list of changes made in the past few months. Here’s a very brief summary:</p><ul><li><p>Hackett includes a <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._.Double%29%29"><code>Double</code></a> type for working with IEEE 754 double-precision floating-point numbers.</p></li><li><p>Local definitions are supported via the <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._let%29%29"><code>let</code></a> and <a href="http://docs.racket-lang.org/hackett/reference-syntactic-forms.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._letrec%29%29"><code>letrec</code></a> forms.</p></li><li><p>The prelude includes many more functions, especially <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28part._reference-lists%29">functions on lists</a>.</p></li><li><p>The Hackett reader has been adjusted to support using <code>.</code> as a bare symbol, since <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._..%29%29"><code>.</code> is the function composition operator</a>.</p></li><li><p>The Hackett REPL supports many more forms, including <a href="http://docs.racket-lang.org/hackett/reference-datatypes.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._data%29%29">ADT</a>, <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._class%29%29">class</a>, and <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28form._%28%28lib._hackett%2Fmain..rkt%29._instance%29%29">instance</a> definitions. Additionally, the REPL now uses <a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a> instances to display the results of expressions. To compensate for the inability to print non-<a href="http://docs.racket-lang.org/hackett/reference-typeclasses.html#%28def._%28%28lib._hackett%2Fmain..rkt%29._.Show%29%29"><code>Show</code></a>able things, a new <code>(#:type expr)</code> syntax is permitted to print the type of <em>any</em> expression.</p></li><li><p>Missing instance errors are now dramatically improved, now correctly highlighting the source location of expressions that led to the error.</p></li></ul><p>Alongside these changes are a variety of internal code improvements that make the Hackett code simpler, more readable, and hopefully more accessible to contributors. Many of the trickiest functions are now <a href="https://github.com/lexi-lambda/hackett/blob/f472859cfc03086d39563e5c0eb81dcb2ceb49dc/hackett-lib/hackett/private/base.rkt#L77-L189">heavily commented</a> with the hope that the codebase won’t be so intimidating to people unfamiliar with Racket or the techniques behind Hackett’s typechecker. I will continue to document the internals of Hackett as I change different places of the codebase, and I have even considered writing a separate Scribble document describing the Hackett internals. It certainly wouldn’t hurt.</p><p>One of the most exciting things about documenting Hackett has been realizing just <em>how much</em> already exists. Seriously, if you have gotten to this point in the blog post but haven’t read <a href="https://pkg-build.racket-lang.org/doc/hackett@hackett-doc/">the actual documentation</a> yet, I would encourage you to do so. No longer does the idea of writing real programs in this language feel out of reach; indeed, aside from potential performance problems, the language is likely extremely close to being usable for very simple things. After all, that’s the goal, isn’t it? As I’ve mentioned before, I’m writing Hackett for other people, but I’m also very much writing it for <em>me</em>: it’s a language I’d like to use.</p><p>Still, writing a general-purpose programming language is a lot of work, and I’ve known from the start that it isn’t something I can accomplish entirely on my own. While this iteration of work on Hackett is a sort of “documentation release”, it might be more accurate to call it an “accessibility release”. If you’re interested in contributing, I finally feel comfortable encouraging you to get involved!</p><h2><a name="a-demo-with-pictures"></a>A demo with pictures</h2><p>Now, if you’re like me, all of this documentation stuff is already pretty exciting. Still, even I view documentation as simply a means to an end, not an end in itself. Documentation is successful when it gets out of the way and makes it possible to write good code that does cool things. Let’s write some, shall we?</p><p>Hackett ships with a special package of demo libraries in the aptly-named <code>hackett-demo</code> package, which are essentially simple, lightweight bindings to existing, dynamically-typed Racket libraries. In <a href="/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/">the previous Hackett blog post</a>, I demonstrated the capabilities of <code>hackett/demo/web-server</code>. In this blog post, we’re going to use <code>hackett/demo/pict</code> and <code>hackett/demo/pict/universe</code>, which make it possible to write interactive, graphical programs in Hackett with just a few lines of code!</p><p>As always, we’ll start with <code>#lang hackett</code>, and we’ll import the necessary libraries:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/pict</span> +<span class="w"> </span><span class="n">hackett/demo/pict/universe</span><span class="p">)</span></code></pre><p>With that, we can start immediately with a tiny example. Just to see how <code>hackett/demo/pict</code> works, let’s start by rendering a red square. We can do this by writing a <code>main</code> action that calls <code>print-pict</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))))</span></code></pre><p>If you run the above program in DrRacket, you should see a 50 pixel red square printed into the interactions window!</p><p></p><p>Using the REPL, we can inspect the type of <code>print-pict</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">print-pict</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Unit</span><span class="p">))</span></code></pre><p>Unsurprisingly, displaying a picture to the screen needs <code>IO</code>. However, what’s interesting is that the rest of the expression is totally pure. Take a look at the type of <code>filled-square</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">filled-square</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Double</span><span class="w"> </span><span class="n">Pict</span><span class="p">)</span></code></pre><p>No <code>IO</code> to be seen! This is because “picts” are entirely <em>pure</em> values that represent images built out of simple shapes, and they can be put together to make more complex images. For example, we can put two squares next to one another:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="n">print-pict</span><span class="w"> </span><span class="p">{(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))</span> +<span class="w"> </span><span class="n">hc-append</span> +<span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">50.0</span><span class="p">))}))</span></code></pre><p>This code will print out a red square to the left of a blue one.</p><p></p><p>Again, <code>hc-append</code> is a simple, pure function, a binary composition operator that places two picts side by side to produce a new one:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="kd">#:type</span><span class="w"> </span><span class="n">hc-append</span><span class="p">)</span> +<span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="p">(</span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="w"> </span><span class="n">Pict</span><span class="p">))</span></code></pre><p>Using the various features of this toolkit, not only can we make interesting pictures and diagrams, we can even create a foundation for a game!</p><h3><a name="implementing-a-snake-clone"></a>Implementing a snake clone</h3><p>This blog post is not a Hackett tutorial; it is merely a demo. For that reason, I am not going to spend much time explaining how the following program is built. This section is closer to annotated source code than a guide to the <code>pict</code> or <code>universe</code> libraries. Hopefully it’s still illustrative.</p><p>We’ll start by writing some type definitions. We’ll need a type to represent 2D points on a grid, as well as a type to represent a cardinal direction (to keep track of which direction the player is moving, for example). We’ll also want an <code>Eq</code> instance for our points.</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">Integer</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="n">d:left</span><span class="w"> </span><span class="n">d:right</span><span class="w"> </span><span class="n">d:up</span><span class="w"> </span><span class="n">d:down</span><span class="p">)</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">Eq</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="k">==</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">d</span><span class="p">)]</span><span class="w"> </span><span class="p">{{</span><span class="n">a</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">c</span><span class="p">}</span><span class="w"> </span><span class="n">&amp;&amp;</span><span class="w"> </span><span class="p">{</span><span class="n">b</span><span class="w"> </span><span class="k">==</span><span class="w"> </span><span class="n">d</span><span class="p">}})])</span></code></pre><p>With these two datatypes, we can implement a <code>move</code> function that accepts a point and a direction and produces a new point for an adjacent tile:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">move</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Direction</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Point</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:left</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:right</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="p">{</span><span class="n">x</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:up</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">})]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">d:down</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">{</span><span class="n">y</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="p">})])</span></code></pre><p>The next step is to define a type for our world state. The <code>big-bang</code> library operates using a game loop, with a function to update the state that’s called each “tick”. Our state will need to hold all the information about our game, which in this case, is just three things:</p><pre><code class="pygments"><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span> +<span class="w"> </span><span class="n">Direction</span><span class="w"> </span><span class="c1">; snake direction</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; snake blocks</span> +<span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="c1">; food blocks</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>It will also be useful to have a functional setter for the direction, which we’ll have to write ourselves, since Hackett does not (currently) have anything like Haskell’s record syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">set-ws-direction</span><span class="w"> </span><span class="p">[[</span><span class="n">d</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)]</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="p">)])</span></code></pre><p>Next, we’ll write some top-level constants that we’ll use in our rendering function, such as the number of tiles in the game board, the size of each tile in pixels, and some simple picts that represent the tiles we’ll use to draw our game:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-width</span><span class="w"> </span><span class="mi">50</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">board-height</span><span class="w"> </span><span class="mi">30</span><span class="p">)</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="p">{(</span><span class="n">d*</span><span class="w"> </span><span class="mf">15.0</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">integer-&gt;double</span><span class="p">})</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="p">(</span><span class="n">blank-rect</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">board-height</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">block</span><span class="w"> </span><span class="p">(</span><span class="n">filled-square</span><span class="w"> </span><span class="mf">13.0</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="n">block</span><span class="p">))</span> +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="p">(</span><span class="n">colorize</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="n">block</span><span class="p">))</span></code></pre><p>Now we can write our actual <code>render</code> function. To do this, we simply need to render each <code>Point</code> in our <code>World-State</code>’s two lists as a block on an <code>empty-board</code>. We’ll write a helper function, <code>render-on-board</code>, which does exactly that:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render-on-board</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">Pict</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">pict</span><span class="w"> </span><span class="n">points</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="nb">foldr</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">point</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">acc</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="n">acc</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">tile-&gt;absolute</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="n">pict</span><span class="p">))</span> +<span class="w"> </span><span class="n">empty-board</span><span class="w"> </span><span class="n">points</span><span class="p">)])</span></code></pre><p>This function uses <code>foldr</code> to collect each point and place the provided pict at the right location using <code>pin-over</code> on an empty board. Using <code>render-on-board</code>, we can write the <code>render</code> function in just a couple of lines:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">render</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Pict</span><span class="p">}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pin-over</span><span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">snake-block</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)</span> +<span class="w"> </span><span class="mf">0.0</span><span class="w"> </span><span class="mf">0.0</span> +<span class="w"> </span><span class="p">(</span><span class="n">render-on-board</span><span class="w"> </span><span class="n">food-block</span><span class="w"> </span><span class="n">food-points</span><span class="p">))])</span></code></pre><p>Next, we’ll need to handle the update logic. On each tick, the snake should advance by a single tile in the direction it’s currently moving. If it runs into a food tile, it should grow one tile larger, and we need to generate a new food tile elsewhere on the board. To help with that last part, the <code>big-bang</code> library provides a <code>random-integer</code> function, which we can use to write a <code>random-point</code> action:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">random-point</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">Point</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="n">point</span><span class="w"> </span><span class="n">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-width</span><span class="p">)</span> +<span class="w"> </span><span class="n">&lt;*&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">random-integer</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="n">board-height</span><span class="p">)})</span></code></pre><p>Hackett supports applicative notation using infix operators, so <code>random-point</code> looks remarkably readable. It also runs in <code>IO</code>, since the result is, obviously, random. Fortunately, the <code>on-tick</code> function runs in <code>IO</code> as well (unlike <code>render</code>, which must be completely pure), so we can use <code>random-point</code> when necessary to generate a new food block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">init!</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">forall</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">{(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">a</span><span class="p">)})</span> +<span class="w"> </span><span class="p">{</span><span class="nb">reverse</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">tail!</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="nb">reverse</span><span class="p">})</span> + +<span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="n">snake-points</span><span class="w"> </span><span class="n">food-points</span><span class="p">)]</span> +<span class="w"> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">new-snake-point</span><span class="w"> </span><span class="p">(</span><span class="n">move</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">(</span><span class="n">head!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">elem?</span><span class="w"> </span><span class="n">food-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">random-point</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">snake-points</span><span class="p">}</span> +<span class="w"> </span><span class="p">{</span><span class="n">new-food-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">delete</span><span class="w"> </span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">food-points</span><span class="p">)})))</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">dir</span><span class="w"> </span><span class="p">{</span><span class="n">new-snake-point</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">init!</span><span class="w"> </span><span class="n">snake-points</span><span class="p">)}</span> +<span class="w"> </span><span class="n">food-points</span><span class="p">))))])</span></code></pre><p>This function is the most complicated one in the whole program, but it’s still not terribly complex. It figures out what the snake’s next location is and binds it to <code>new-snake-point</code>, then checks if there is a food block at that location. If there is, it generates a <code>new-food-point</code>, then puts it in the new world state. Otherwise, it removes the last snake point and continues as usual.</p><p>The game is already almost completely written. The next step is just to handle key events, which are obviously important for allowing the player to control the snake. Fortunately, this is easy, since we can just use our <code>set-ws-direction</code> function that we wrote earlier:</p><pre><code class="pygments"><span class="p">(</span><span class="n">defn</span><span class="w"> </span><span class="n">on-key</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">{</span><span class="n">KeyEvent</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">World-State</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">IO</span><span class="w"> </span><span class="n">World-State</span><span class="p">)}</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:left</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:left</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:right</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:right</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:up</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:up</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="n">ke:down</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">set-ws-direction</span><span class="w"> </span><span class="n">d:down</span><span class="p">)}]</span> +<span class="w"> </span><span class="p">[[</span><span class="k">_</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="n">pure</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">id</span><span class="p">}])</span></code></pre><p>The <code>on-key</code> function runs in <code>IO</code>, but we don’t actually need that power, since all of our keypress update logic is completely pure, so we just wrap everything in <code>pure</code>.</p><p>We’re almost done now—all we need to do is set up the <em>initial</em> state when the game begins. We’ll write a small binding that creates a world state with the snake in the middle of the board and some random food locations scattered about:</p><pre><code class="pygments"><span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">initial-state</span> +<span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">initial-food</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="n">sequence</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">(</span><span class="n">repeat</span><span class="w"> </span><span class="n">random-point</span><span class="p">)))]</span> +<span class="w"> </span><span class="p">(</span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">world-state</span><span class="w"> </span><span class="n">d:right</span> +<span class="w"> </span><span class="p">{(</span><span class="n">point</span><span class="w"> </span><span class="mi">25</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">24</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">point</span><span class="w"> </span><span class="mi">23</span><span class="w"> </span><span class="mi">15</span><span class="p">)</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="n">nil</span><span class="p">}</span> +<span class="w"> </span><span class="n">initial-food</span><span class="p">))))</span></code></pre><p>Notably, we can use the <code>repeat</code> function to create an infinite list of <code>random-point</code> actions, <code>take</code> the first five of them, then call <code>sequence</code> to execute them from left to right. Now, all we have to do is put the pieces together in a <code>main</code> block:</p><pre><code class="pygments"><span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">[</span><span class="n">state</span><span class="w"> </span><span class="n">&lt;-</span><span class="w"> </span><span class="n">initial-state</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="n">big-bang</span><span class="w"> </span><span class="n">state</span> +<span class="w"> </span><span class="kd">#:to-draw</span><span class="w"> </span><span class="n">render</span> +<span class="w"> </span><span class="kd">#:on-tick</span><span class="w"> </span><span class="n">on-tick</span><span class="w"> </span><span class="mf">0.2</span> +<span class="w"> </span><span class="kd">#:on-key</span><span class="w"> </span><span class="n">on-key</span><span class="p">)))</span></code></pre><p>And that’s it! We haven’t implemented any win or loss conditions, but the basics are all there. In 80 lines of code, we’ve implemented a working snake game in Hackett.</p><p></p><h2><a name="contributing-to-hackett"></a>Contributing to Hackett</h2><p>If you are excited enough about Hackett to be interested in contributing, your first question is very likely “What can I do?” or “Where do I start?” My answer to that is (perhaps a little unhelpfully): it depends! My general recommendation is to try and write something with Hackett, and if you run into anything that prevents you from accomplishing your goal, look into what would need to be changed to support your program. Having a use case is a great way to come up with useful improvements.</p><p>On the other hand, you might not have anything in mind, or you might find Hackett’s scope a little too overwhelming to just jump right in and start contributing. Fortunately, <a href="https://github.com/lexi-lambda/hackett/issues">Hackett has an issue tracker</a>, so feel free to take a look and pick something that looks interesting and achievable. Alternatively, the standard library can always use fleshing out, and quite a lot of that can be written without ever even touching the scary Hackett internals.</p><p>Additionally, if you have any questions, please don’t hesitate to ask them! If you have a question about the codebase, get stuck implementing something, or just don’t know where to start, feel free to <a href="https://github.com/lexi-lambda/hackett/issues">open an issue on GitHub</a>, send me a message on the <code>#racket</code> IRC channel on Freenode, or ping me on <a href="http://racket-slack.herokuapp.com">the Racket Slack team</a>.</p><h2><a name="acknowledgements"></a>Acknowledgements</h2><p>Speaking of contributors, I’m excited to say that this is the first time I can truly say Hackett includes code written by someone other than me! I want to call attention to <a href="https://github.com/gelisam">Samuel Gélineau, aka gelisam</a>, who is officially the second contributor to Hackett. He helped to implement the new approach the Hackett REPL uses for printing expressions, which ended up being quite useful when implementing some of the other REPL improvements.</p><p>Additionally, I want to specially thank <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for being especially responsive and helpful to my many questions about Scribble and the Racket top level. Racket continues to be extremely impressive, both as a project and as a community.</p><p>Finally, many thanks to the various people who have expressed interest in the project and continue to push me and ask questions. Working on Hackett is a lot of work—both time and effort—and it’s your continued enthusiasm that inspires me to put in the hours.</p><ol class="footnotes"></ol></article>User-programmable infix operators in Rackethttps://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/https://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/12 Aug 2017<article><p>Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in <a href="https://github.com/lexi-lambda/hackett">Hackett</a>, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.</p><p>Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done <em>without</em> modifying the stock <code>#lang racket</code> reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.</p><h2><a name="our-mission"></a>Our mission</h2><p>Before we embark, let’s clarify our goal. We want to support infix operators in Racket, of course, but that could mean a lot of different things! Let’s start with what we <em>do</em> want:</p><ul><li><p>Infix operators should be user-extensible, not limited to a special set of built-in operators.</p></li><li><p>Furthermore, operators’ names should not be restricted to a separate “operator” character set. Any valid Lisp identifier should be usable as an infix operator.</p></li><li><p>We want to be able to support fixity/associativity annotations. Some operators should associate to the left, like subtraction, but others should associate to the right, like <code>cons</code>. This allows <code>5 - 1 - 2</code> to be parsed as <code>(- (- 5 1) 2)</code>, but <code>5 :: 1 :: nil</code> to be parsed as <code>(:: 5 (:: 1 nil))</code>.</p></li></ul><p>These are nice goals, but we also won’t be too ambitious. In order to keep things simple and achievable, we’ll keep the following restrictions:</p><ul><li><p>We will <strong>not</strong> permit infix expressions in arbitrary locations, since that would be impossible to parse given how we want to allow users to pick any names for operators they wish. Instead, infix expressions must be wrapped in curly braces, e.g. replacing <code>(+ 1 2)</code> with <code>{1 + 2}</code>.</p></li><li><p>Our implementation will <strong>not</strong> support any notion of operator precedence; all operators will have equal precedence, and it will be illegal to mix operators of different associativity in the same expression. Precedence is entirely possible to implement in theory, but it would be considerably more work, so this blog post does not include it.</p></li><li><p>All operators will be binary, and we will <strong>not</strong> support unary or mixfix operators. My intuition is that this technique should be able to be generalized to both of those things, but it would be considerably more complicated.</p></li></ul><p>With those points in mind, what would the interface for our infix operator library look like for our users? Ideally, something like this:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="s2">"infix.rkt"</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> + +<span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">{</span><span class="mi">10</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="c1">; =&gt; &#39;(1 7)</span></code></pre><p>Let’s get started.</p><h2><a name="implementing-infix-operators"></a>Implementing infix operators</h2><p>Now that we know what we want, how do we get there? Well, there are a few pieces to this puzzle. We’ll need to solve a two main problems:</p><ol><li><p>How do we “hook into” expressions wrapped with curly braces so that we can perform a desugaring pass?</p></li><li><p>How can we associate fixity information with certain operators?</p></li></ol><p>We’ll start by tackling the first problem, since its solution will inform the answer to the second. Since we won’t have any fixity information to start with, we’ll just assume that all operators associate left by default.</p><p>So, how <em>do</em> we detect if a Racket expression is surrounded by curly braces? Normally, in <code>#lang racket</code>, parentheses, square brackets, and curly braces are all interchangeable. Indeed, if you use curly braces in the REPL, you will find that they are treated <em>exactly</em> the same as parentheses:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>If they are treated identically, giving them special behavior might seem hopeless, but don’t despair! Racket is no ordinary programming language, and it provides some tools to help us out here.</p><p>Someone who has worked with Lisps before is likely already aware that Lisp source code is a very direct representation of its AST, composed mostly of lists, pairs, symbols, numbers, and strings. In Racket, this is also true, but Racket also wraps these datums in boxes known as <a href="http://docs.racket-lang.org/reference/syntax-model.html#%28tech._syntax._object%29"><em>syntax objects</em></a>. Syntax objects contain extra metadata about the code, most notably its lexical context, necessary for Racket’s hygiene system. However, syntax objects can also contain arbitrary metadata, known as <a href="http://docs.racket-lang.org/reference/stxprops.html#%28tech._syntax._property%29"><em>syntax properties</em></a>. Macros can attach arbitrary values to the syntax objects they produce using syntax properties, and other macros can inspect them. Racket’s <a href="http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html#%28tech._reader%29"><em>reader</em></a> (the syntax parser that turns program text into Racket syntax objects) also attaches certain syntax properties as part of its parsing process. One of those is named <a href="http://docs.racket-lang.org/reference/reader.html#%28idx._%28gentag._30._%28lib._scribblings%2Freference%2Freference..scrbl%29%29%29"><code>'paren-shape</code></a>.</p><p>This syntax property, as the name implies, keeps track of the shape of parentheses in syntax objects. You can see that for yourself by inspecting the property’s value for different syntax objects in the REPL:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="no">#f</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\[</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-property</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">paren-shape</span><span class="p">)</span> +<span class="sc">#\{</span></code></pre><p>This syntax property gives us the capability to distinguish between syntax objects that use curly braces and those that don’t, which is a step in the right direction, but it still doesn’t give us any hook with which we can change the behavior of certain expressions. Fortunately, there’s something else that can.</p><h3><a name="customizing-application"></a>Customizing application</h3><p>Racket is a language <em>designed</em> to be extended, and it provides a variety of hooks in the language for the purposes of tweaking pieces in minor ways. One such hook is named <a href="http://docs.racket-lang.org/reference/application.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25app%29%29"><code>#%app</code></a>, which is automatically introduced by the macroexpander whenever it encounters a function application. That means it effectively turns this:</p><pre><code class="pygments"><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>…into this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">#%app</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span></code></pre><p>What’s special about <code>#%app</code> is that the macroexpander will use whichever <code>#%app</code> is in scope in the expression’s lexical context, so if we write our own version of <code>#%app</code>, it will be used instead of the one from <code>#lang racket</code>. This is what we will use to hook into ordinary Racket expressions.</p><p>To write our custom version of <code>#%app</code>, we will use the usual tool: Racket’s industrial-strength macro-authoring DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. We’ll also use a helper library that provides some tools for pattern-matching on syntax objects with the <code>'paren-shape</code> syntax property, <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Fparen-shape%29"><code>syntax/parse/class/paren-shape</code></a>. Using these, we can transform expressions that are surrounded in curly braces differently from how we would transform expressions surrounded by parentheses:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>This code will transform any applications surrounded in curly braces into one that starts with <code>#%infix</code> instead of <code>#%app</code>, so <code>{1 + 2}</code> will become <code>(#%infix 1 + 2)</code>, for example. The identifier <code>#%infix</code> isn’t actually special in any way, it just has a funny name, but we haven’t actually defined <code>#%infix</code> yet, so we need to do that next!</p><p>To start, we’ll just handle the simplest case: infix expressions with precisely three subexpressions, like <code>{1 + 2}</code>, should be converted into the equivalent prefix expressions, in this case <code>(+ 1 2)</code>. We can do this with a simple macro:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)])</span></code></pre><p>Due to the way Racket propagates syntax properties, we explicitly indicate that the resulting expansion should use the <code>#%app</code> from <code>racket/base</code>, which will avoid any accidental infinite recursion between our <code>#%app</code> and <code>#%infix</code>. With this in place, we can now try our code out in the REPL, and believe it or not, we now support infix expressions with just those few lines of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span> +<span class="mi">3</span></code></pre><p>That’s pretty cool!</p><p>Of course, we probably want to support infix applications with more than just a single binary operator, such as <code>{1 + 2 + 3}</code>. We can implement that just by adding another case to <code>#%infix</code> that handles more subforms:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span></code></pre><p>…and now, just by adding those two lines, we support arbitrarily-large sequences of infix operators:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">10</span></code></pre><p>I don’t know about you, but I think being able to do this in less than 20 lines of code is pretty awesome. We can even mix different operators in the same expression:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">5</span></code></pre><p>Of course, all of our infix expressions currently assume that all operators associate left, as was our plan. In general, though, there are lots of useful operators that associate right, such as <code>cons</code>, nested <code>-&gt;</code> types or contracts for curried functions, and <code>expt</code>, the exponentiation operator.</p><h3><a name="tracking-operator-fixity"></a>Tracking operator fixity</h3><p>Clearly, we need some way to associate operator fixity with certain identifiers, and we need to be able to do it at compile-time. Fortunately, Racket has a very robust mechanism for creating compile-time values. Unfortunately, simply associating metadata with an identifier is a little less convenient than it could be, but there is a general technique that can be done with little boilerplate.</p><p>Essentially, Racket (like Scheme) uses a <code>define-syntax</code> form to define macros, which is what <code>define-syntax-parser</code> eventually expands into. However, unlike Scheme, Racket’s <code>define-syntax</code> is not <em>just</em> for defining macros—it’s for defining arbitrary bindings with compile-time (aka “phase 1”) values. Using this, we can define bindings that have entirely arbitrary values at compile-time, including plain data like numbers or strings:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Once a binding has been defined using <code>define-syntax</code>, a macro can look up the value associated with it by using the <a href="http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29"><code>syntax-local-value</code></a> function, which returns the compile-time value associated with an identifier:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">foo</span><span class="p">)))</span> +<span class="c1">; =&gt; 3</span></code></pre><p>The cool thing is that <code>syntax-local-value</code> gets the value associated with a specific <em>binding</em>, not a specific name. This means a macro can look up the compile-time value associated with an identifier provided to it as a subform. This is close to what we want, since we could use <code>syntax-local-value</code> to look up something associated with our infix operator bindings, but the trouble is that they would then cease to be usable as ordinary functions. For example, if you try and use the <code>foo</code> binding from the above example as an expression, Racket will complain about an “illegal use of syntax”, which makes sense, because <code>foo</code> is not bound to anything at runtime.</p><p>To solve this problem, we can use something of a trick: any compile-time binding that happens to have a procedure as its value will be treated like a macro—that is, using it as an expression will cause the macroexpander to invoke the procedure with a syntax object representing the macro invocation, and the procedure is expected to produce a new syntax object as output. Additionally, Racket programmers can make custom datatypes valid procedures by using the <a href="http://docs.racket-lang.org/reference/procedures.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._prop~3aprocedure%29%29"><code>prop:procedure</code></a> structure type property.</p><p>If you are not familiar with the Racket macro system, this probably sounds rather complicated, but in practice, it’s not as confusing as it might seem. The trick here is to create a custom structure type at compile-time that we can use to track operator fixity alongside its runtime binding:</p><pre><code class="pygments"><span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/transformer</span><span class="p">))</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">))))</span></code></pre><p>This is quite the magical incantation, and all the details of what is going on here are outside the scope of this blog post. Essentially, though, we can use values of this structure as a compile-time binding that will act just like the identifier provided for <code>runtime-binding</code>, but we can also include a value of our choosing for <code>fixity</code>. Here’s an example:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="nb">cons</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="p">))</span></code></pre><p>This new <code>::</code> binding will act, in every way, just like <code>cons</code>. If we use it in the REPL, you can see that it acts exactly the same:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span></code></pre><p>However, we can also use <code>syntax-local-value</code> to extract this binding’s fixity at compile-time, and that’s what makes it interesting:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">::</span><span class="p">))))</span> +<span class="c1">; =&gt; &#39;right</span></code></pre><p>Using this extra compile-time information, we can adjust our <code>#%infix</code> macro to inspect bindings and determine their fixity, then use that to make decisions about parsing. Just like we used <code>syntax/parse/class/paren-shape</code> to make decisions based on the <code>'paren-shape</code> syntax property, we can use <a href="http://docs.racket-lang.org/syntax-classes/index.html#%28mod-path._syntax%2Fparse%2Fclass%2Flocal-value%29"><code>syntax/parse/class/local-value</code></a> to pattern-match on bindings with a particular compile-time value. We’ll wrap this in a syntax class of our own to make the code easier to read:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span></code></pre><p>Now, we can update <code>#%infix</code> to use our new <code>infix-op</code> syntax class:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span></code></pre><p>Notably, we now require all operators to be bound to compile-time infix operator values, and we include two conditions via <code>#:when</code> clauses. These clauses check to ensure that the operator in question has the expected fixity before committing to that clause; if the condition fails, then parsing backtracks. Using this new definition of <code>#%infix</code>, we can successfully use <code>::</code> in an infix expression, and it will be parsed with the associativity that we expect:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()}</span> +<span class="o">&#39;</span><span class="p">(</span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span></code></pre><p>Exciting!</p><h3><a name="a-nicer-interface-for-defining-infix-operators"></a>A nicer interface for defining infix operators</h3><p>We currently have to define infix operators by explicitly using <code>define-syntax</code>, but this is not a very good interface. Users of infix syntax probably don’t want to have to understand the internal workings of the infix operator implementation, so we just need to define one final macro to consider this done: the <code>define-infix-operator</code> form from the example at the very beginning of this blog post.</p><p>Fortunately, this macro is absolutely trivial to write. In fact, we can do it in a mere three lines of code, since it’s very minor sugar over the <code>define-syntax</code> definitions we were already writing:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span></code></pre><p>With this in hand, we can define some infix operators with a much nicer syntax:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>With these simple definitions, we can write some very nice mathematical expressions that use infix syntax, in ordinary <code>#lang racket</code>:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="mi">4</span><span class="p">}</span> +<span class="mi">-1</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">256</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">{{</span><span class="mi">2</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">2</span><span class="p">}</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="mi">64</span></code></pre><p>And you know what’s most amazing about this? The entire thing is <strong>only 50 lines of code</strong>. Here is the entire implementation of infix operators from this blog post in a single code block, with absolutely nothing hidden or omitted:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="p">(</span><span class="k">for-syntax</span><span class="w"> </span><span class="n">syntax/parse/class/local-value</span> +<span class="w"> </span><span class="n">syntax/parse/class/paren-shape</span> +<span class="w"> </span><span class="n">syntax/transformer</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">prefix-in</span><span class="w"> </span><span class="n">racket/base/</span><span class="w"> </span><span class="n">racket/base</span><span class="p">)</span> +<span class="w"> </span><span class="n">syntax/parse/define</span><span class="p">)</span> + +<span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">infix-operator</span><span class="w"> </span><span class="p">(</span><span class="n">runtime-binding</span><span class="w"> </span><span class="n">fixity</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">operator</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">((</span><span class="nb">set!-transformer-procedure</span> +<span class="w"> </span><span class="p">(</span><span class="n">make-variable-like-transformer</span> +<span class="w"> </span><span class="p">(</span><span class="n">infix-operator-runtime-binding</span><span class="w"> </span><span class="n">operator</span><span class="p">)))</span> +<span class="w"> </span><span class="n">stx</span><span class="p">)))</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">infix-op</span> +<span class="w"> </span><span class="kd">#:description</span><span class="w"> </span><span class="s2">"infix operator"</span> +<span class="w"> </span><span class="kd">#:attributes</span><span class="w"> </span><span class="p">[</span><span class="n">fixity</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">pattern</span><span class="w"> </span><span class="p">{</span><span class="n">~var</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">local-value</span><span class="w"> </span><span class="n">infix-operator?</span><span class="p">)}</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator-fixity</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.local-value</span><span class="p">))]))</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="k">#%app</span> +<span class="w"> </span><span class="p">[{</span><span class="n">~braces</span><span class="w"> </span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">}</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> + +<span class="p">(</span><span class="n">define-syntax-parser</span><span class="w"> </span><span class="n">#%infix</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">racket/base/#%app</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">left</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op:infix-op</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:when</span><span class="w"> </span><span class="p">(</span><span class="nb">eq?</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">right</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">op.fixity</span><span class="p">))</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">...</span><span class="w"> </span><span class="p">(</span><span class="n">#%infix</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="n">b</span><span class="p">))])</span> + +<span class="p">(</span><span class="n">define-simple-macro</span><span class="w"> </span><span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">op:id</span><span class="w"> </span><span class="n">value:id</span> +<span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}})</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">value</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)))</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">racket/base/+</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">-</span><span class="w"> </span><span class="n">racket/base/-</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="n">racket/base/*</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="n">racket/base//</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">left</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">^</span><span class="w"> </span><span class="nb">expt</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span> +<span class="p">(</span><span class="n">define-infix-operator</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="nb">cons</span><span class="w"> </span><span class="kd">#:fixity</span><span class="w"> </span><span class="n">right</span><span class="p">)</span></code></pre><p>Racket is a hell of a programming language.</p><h2><a name="applications-limitations-and-implications"></a>Applications, limitations, and implications</h2><p>This blog post has outlined a complete, useful model for infix operators, and it is now hopefully clear how they work, but many of the most interesting properties of this implementation are probably not obvious. As far as I can make out, this embedding of infix operators into a macro system is novel, and I am <em>almost certain</em> that the way this implementation tracks fixity information is unique. One of the most interesting capabilities gained from this choice of implementation is the ability for macros to define infix operators and control their fixity, even <em>locally</em>.</p><p>What does this mean? Well, remember that infix operators are just special syntax bindings. Racket includes a variety of forms for binding or adjusting macros locally, such as <code>let-syntax</code> and <code>syntax-parameterize</code>. Using these tools, it would be entirely possible to implement a <code>with-fixity</code> macro, that could adjust the fixity of an operator within a syntactic block. This could be used, for example, to make <code>/</code> right associative within a block of code:</p><pre><code class="pygments"><span class="nb">&gt;</span><span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span> +<span class="m">1/6</span> +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="nb">/</span><span class="w"> </span><span class="n">right</span><span class="p">])</span> +<span class="w"> </span><span class="p">{</span><span class="mi">1</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nb">/</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span> +<span class="mi">1</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>In fact, this macro is hardly theoretical, since it could be implemented in a trivial 7 lines, simply expanding to uses of <code>splicing-let</code> and <code>splicing-let-syntax</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-simple-macro</span> +<span class="w"> </span><span class="p">(</span><span class="n">with-fixity</span><span class="w"> </span><span class="p">([</span><span class="n">op:id</span><span class="w"> </span><span class="p">{</span><span class="n">~and</span><span class="w"> </span><span class="n">fixity</span><span class="w"> </span><span class="p">{</span><span class="n">~or</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">left</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="n">~datum</span><span class="w"> </span><span class="n">right</span><span class="p">}}}]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:with</span><span class="w"> </span><span class="p">[</span><span class="n">op-tmp</span><span class="w"> </span><span class="k">...</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">[</span><span class="n">op</span><span class="w"> </span><span class="k">...</span><span class="p">])</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let</span><span class="w"> </span><span class="p">([</span><span class="n">op-tmp</span><span class="w"> </span><span class="n">op</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">splicing-let-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">op</span><span class="w"> </span><span class="p">(</span><span class="n">infix-operator</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">op-tmp</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">fixity</span><span class="p">)]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">)))</span></code></pre><p>This is not especially useful given the current set of infix operator features, but it’s easy to imagine how useful it could be in a system that also supported a notion of precedence. It is not entirely uncommon to encounter certain expressions that could be more cleanly expressed with a local set of operator precedence rules, perhaps described as a set of relations <em>between</em> operators rather than a global table of magic precedence numbers. With traditional approaches to infix operators, parsing such code would be difficult without a very rigid syntactic structure, but this technique makes it easy.</p><p>As mentioned at the beginning of this blog post, this technique is also not merely a novelty—as of now, I am actively using this in <a href="https://github.com/lexi-lambda/hackett">Hackett</a> to support infix operators with all of the features outlined here. The Hackett implementation is a little bit fancier than the one in this blog post, since it works harder to produce better error messages. It explicitly disallows mixing left associative and right associative operators in the same expression, so it does some additional validation as part of expansion, and it arranges for source location information to be copied onto the result. It also make a different design decision to allow <em>any</em> expression to serve as an infix operator, assuming left associativity if no fixity annotation is available.</p><p>If you’re interested in the code behind the additional steps Hackett takes to make infix operators more usable and complete, take a look at <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/infix.rkt">this file for the definition of infix bindings</a>, as well as <a href="https://github.com/lexi-lambda/hackett/blob/0d177d00a9ee96f30dd76761f1cb86f15830779f/hackett-lib/hackett/private/kernel.rkt#L80-L101">this file for the defintion of infix application</a>. My hope is to eventually add support for some sort of precedence information, though who knows—maybe infix operators will be easier to reason about if the rules are kept extremely simple. I am also considering adding support for so-called “operator sections” at some point, which would allow things like <code>{_ - 1}</code> to serve as a shorthand for <code>(lambda [x] {x - 1})</code>, but I haven’t yet decided if I like the tradeoffs involved.</p><p>It’s possible that this implementation of infix operators might also be useful in languages in the Racket ecosystem besides Hackett. However, I’m not sure it makes a ton of sense in <code>#lang racket</code> without modifications, as variadic functions subsume many of the cases where infix operators are needed in Haskell. If there is a clamoring for this capability, I would be happy to consider extracting the functionality into a library, but as of right now, I don’t have any plans to do so.</p><p>Finally, the main point of this blog post is to showcase how easy it is to do things in Racket that would be impossible in most languages and difficult even in most Lisps. It also helps to show off how Hackett is already benefitting from those capabilities: while this particular feature is built-in to <code>#lang hackett</code>, there’s no reason something similar but more powerful couldn’t be built as a separate library by a <em>user</em> of Hackett. Even as Hackett’s author, I think that’s exciting, since makes it possible for users to experiment with improvements to the language on their own. Some of those improvements may eventually be rolled into the core language or standard library, but many of them can likely live effectively in separate libraries, accessible on-demand to those who need them. After all, that’s one of Racket’s most important promises—languages as libraries—and it’s why Hackett is a part of the Racket ecosystem.</p><ol class="footnotes"></ol></article>Realizing Hackett, a metaprogrammable Haskellhttps://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/https://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/27 May 2017<article><p><a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">Almost five months ago, I wrote a blog post about my new programming language, Hackett</a>, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.</p><p>Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, <a href="https://github.com/lexi-lambda/hackett">Hackett is not only real, it’s working, and you can try it out yourself</a>!</p><h2><a name="a-first-look-at-hackett"></a>A first look at Hackett</h2><p>Hackett is still very new, very experimental, and an enormous work in progress. However, that doesn’t mean it’s useless! Hackett is already a remarkably capable programming language. Let’s take a quick tour.</p><p>As Racket law decrees it, every Hackett program must begin with <code>#lang</code>. We can start with the appropriate incantation:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span></code></pre><p>If you’re using DrRacket or racket-mode with background expansion enabled, then congratulations: the typechecker is online. We can begin by writing a well-typed, albeit boring program:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">))</span></code></pre><p>In Hackett, a use of <code>main</code> at the top level indicates that running the module as a program should execute some <code>IO</code> action. In this case, <code>println</code> is a function of type <code>{String -&gt; (IO Unit)}</code>. Just like Haskell, Hackett is pure, and the runtime will figure out how to actually run an <code>IO</code> value. If you run the above program, you will notice that it really does print out <code>Hello, world!</code>, exactly as we would like.</p><p>Of course, hello world programs are boring—so imperative! We are functional programmers, and we have our <em>own</em> class of equally boring programs we must write when learning a new language. How about some Fibonacci numbers?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="n">def</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">List</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span> +<span class="w"> </span><span class="p">{</span><span class="mi">0</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">::</span><span class="w"> </span><span class="p">(</span><span class="n">zip-with</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="n">fibs</span><span class="w"> </span><span class="p">(</span><span class="n">tail!</span><span class="w"> </span><span class="n">fibs</span><span class="p">))})</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="p">(</span><span class="n">show</span><span class="w"> </span><span class="p">(</span><span class="nb">take</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="n">fibs</span><span class="p">))))</span></code></pre><p>Again, Hackett is just like Haskell in that it is <em>lazy</em>, so we can construct an infinite list of Fibonacci numbers, and the runtime will happily do nothing at all. When we call <code>take</code>, we realize the first ten numbers in the list, and when you run the program, you should see them printed out, clear as day!</p><p>But these programs are boring. Printing strings and laziness may have been novel when you first learned about them, but if you’re reading this blog post, my bet is that you probably <em>aren’t</em> new to programming. How about something more interesting, <strong>like a web server</strong>?</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">hackett</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">hackett/demo/web-server</span><span class="p">)</span> + +<span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="w"> </span><span class="n">String</span><span class="p">))</span> + +<span class="p">(</span><span class="n">instance</span><span class="w"> </span><span class="p">(</span><span class="n">-&gt;Body</span><span class="w"> </span><span class="n">Greeting</span><span class="p">)</span> +<span class="w"> </span><span class="p">[</span><span class="n">-&gt;body</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">[(</span><span class="n">greeting</span><span class="w"> </span><span class="n">name</span><span class="p">)]</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hello, "</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="n">++</span><span class="w"> </span><span class="s2">"!"</span><span class="p">})])</span> + +<span class="p">(</span><span class="n">defserver</span><span class="w"> </span><span class="n">run-server</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"/"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="s2">"Hello, world!"</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">GET</span><span class="w"> </span><span class="s2">"greet"</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="k">=&gt;</span><span class="w"> </span><span class="n">greeting</span><span class="p">])</span> + +<span class="p">(</span><span class="n">main</span><span class="w"> </span><span class="p">(</span><span class="k">do</span><span class="w"> </span><span class="p">(</span><span class="nb">println</span><span class="w"> </span><span class="s2">"Running server on port 8080."</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">run-server</span><span class="w"> </span><span class="mi">8080</span><span class="p">)))</span></code></pre><pre><code class="pygments">$<span class="w"> </span>racket<span class="w"> </span>my-server.rkt +Running<span class="w"> </span>server<span class="w"> </span>on<span class="w"> </span>port<span class="w"> </span><span class="m">8080</span>. +^Z +$<span class="w"> </span><span class="nb">bg</span> +$<span class="w"> </span>curl<span class="w"> </span><span class="s1">&#39;http://localhost:8080/greet/Alexis&#39;</span> +Hello,<span class="w"> </span>Alexis!</code></pre><p><strong>Welcome to Hackett.</strong></p><h2><a name="what-is-hackett"></a>What is Hackett?</h2><p>Excited yet? I hope so. I certainly am.</p><p>Before you get a little <em>too</em> excited, however, let me make a small disclaimer: the above program, while quite real, is a demo. It is certainly not a production web framework, and it actually just uses the Racket web server under the hood. It does not handle very many things right now. You cannot use it to build your super awesome webapp, and even if you could, I would not recommend attempting to do so.</p><p>All that said, it is a <em>real</em> tech demo, and it shows off the potential for Hackett to do some pretty cool things. While the server implementation is just reusing Racket’s dynamically typed web server, the Hackett interface to it is 100% statically typed, and the above example shows off a host of features:</p><ul><li><p><strong>Algebraic datatypes.</strong> Hackett has support for basic ADTs, including recursive datatypes (though not yet mutually recursive datatypes).</p></li><li><p><strong>Typeclasses.</strong> The demo web server uses a <code>-&gt;Body</code> typeclass to render server responses, and this module implements a <code>-&gt;Body</code> instance for the custom <code>Greeting</code> datatype.</p></li><li><p><strong>Macros.</strong> The <code>defserver</code> macro provides a concise, readable, <em>type safe</em> way to define a simple, RESTful web server. It defines two endpoints, a homepage and a greeting, and the latter parses a segment from the URL.</p></li><li><p><strong>Static typechecking.</strong> Obviously. If you try and change the homepage endpoint to produce a number instead of a string, you will get a type error! Alternatively, try removing the <code>-&gt;Body</code> instance and see what happens.</p></li><li><p><strong>Infix operators.</strong> In Hackett, <code>{</code> curly braces <code>}</code> enter <em>infix mode</em>, which permits arbitrary infix operators. Most Lisps have variadic functions, so infix operators are not strictly necessary, but Hackett only supports curried, single-argument functions, so infix operators are some especially sweet sugar.</p></li><li><p><strong>Pure, monadic I/O.</strong> The <code>println</code> and <code>run-server</code> functions both produce <code>(IO Unit)</code>, and <code>IO</code> is a monad. <code>do</code> notation is provided as a macro, and it works with any type that implements the <code>Monad</code> typeclass.</p></li></ul><p>All these features are already implemented, and they really work! Of course, you might look at this list and be a little confused: sure, there are macros, but all these other things are firmly Haskellisms. If you thought that, you’d be quite right! <strong>Hackett is much closer to Haskell than Racket, even though it is syntactically a Lisp.</strong> Keep this guiding principal in mind as you read this blog post or explore Hackett. Where Haskell and Racket conflict, Hackett usually prefers Haskell.</p><p>For a bit more information about what Hackett is and what it aims to be, <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">check out my blog post from a few months ago</a> from back when Hackett was called Rascal. I won’t reiterate everything I said there, but I do want to give a bit of a status update, explain what I’ve been working on, and hopefully give you some idea about where Hackett is going.</p><h2><a name="the-story-so-far-and-getting-to-hackett-0-1"></a>The story so far, and getting to Hackett 0.1</h2><p>In September of 2016, I attended <a href="http://con.racket-lang.org/2016/">(sixth RacketCon)</a>, where I saw a <a href="https://www.youtube.com/watch?v=j5Hauz6cewM">pretty incredible and extremely exciting talk</a> about implementing type systems as macros. Finally, I could realize my dream of having an elegant Lisp with a safe, reliable macro system and a powerful, expressive type system! Unfortunately, reality ensued, and I remembered I didn’t actually know any type theory.</p><p>Therefore, in October, I started to learn about type systems, and I began to read through Pierce’s Types and Programming Languages, then tried to learn the things I would need to understand Haskell’s type system. I learned about Hindley-Milner and basic typeclasses, and I tried to apply these things to the Type Systems as Macros approach. Throughout October, I hacked and I hacked, and by the end of the month, I stood back and admired my handiwork!</p><p>…it <em>sort of</em> worked?</p><p>The trouble was that I found myself stuck. I wasn’t sure how to proceed. My language had bugs, programs sometimes did things I didn’t understand, the typechecker was clearly unsound, and there didn’t seem to be an obvious path forward. Other things in my life became distracting or difficult, and I didn’t have the energy to work on it anymore, so I stopped. I put Hackett (then Rascal) on the shelf for a couple months, only to finally return to it in late December.</p><p>At the beginning of January, I decided it would be helpful to be public about what I was working on, so I wrote a blog post! Feedback was positive, overwhelmingly so, and while it was certainly encouraging, I suddenly felt nervous about expectations I had not realized I was setting. Could I really build this? Did I have the knowledge or the time? At that point, I didn’t really, so work stalled.</p><p>Fortunately, in early April, some things started to become clear. I took another look at Hackett, and I knew I needed to reimplement it from the ground up. I also knew that I needed a different technique, but this time, I knew a bit more about where to find it. I got some help from <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> and put together <a href="https://gist.github.com/lexi-lambda/045ba782c8a0d915bd8abf97167d3bb5">an implementation of Pierce and Turner’s Local Type Inference</a>. Unfortunately, it didn’t really provide the amount of type inference I was looking for, but fortunately, implementing it helped me figure out how to understand the rather more complicated (though very impressive) <a href="http://www.cs.cmu.edu/~joshuad/papers/bidir/">Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism</a>. After that, things just sort of started falling into place:</p><ul><li><p>First, I <a href="https://github.com/lexi-lambda/higher-rank">implemented the Complete and Easy paper in Haskell</a>, including building a little parser and interpreter. That helped me actually understand the paper, and Haskell really is a rather wonderful language for doing such a thing.</p></li><li><p>Three days later, I <a href="https://github.com/lexi-lambda/racket-higher-rank">ported the Haskell implementation to Racket</a>, using (and somewhat abusing) the Type Systems as Macros techniques. It wasn’t the prettiest, but it seemed to work, and that was rather encouraging.</p></li><li><p>After that, however, I got a little stuck again, as I wasn’t sure how to generalize what I had. I was also incredibly busy with my day job, and I wasn’t able to really make progress for a few weeks. In early May, however, I decided to <a href="https://twitter.com/lexi_lambda/status/865026650487967744">take a vacation</a> for a week, and with some time to focus, I <a href="https://github.com/lexi-lambda/higher-rank/tree/algebraic">souped up the Haskell implementation with products and sums</a>. This was progress!</p></li><li><p>The <em>following day</em> I managed to make <a href="https://github.com/lexi-lambda/racket-higher-rank/tree/type-constructors">similar changes to the Racket implementation</a>, but rather than add anonymous products and sums, I added arbitrary type constructors.</p></li><li><p>A couple days later and with more than a bit of help from <a href="http://functorial.com">Phil Freeman</a>, I <a href="https://github.com/lexi-lambda/hackett/commit/1fd7fc905b93f68e39b9d01fedc4fb52aa44c4c4">rebranded the Racket implementation as Hackett, Mk II</a>, and I started working towards turning it into a real programming language.</p></li></ul><p><em>Less than three weeks later</em>, and I have a programming language with everything from laziness and typeclasses to a tiny, proof-of-concept web server with <a href="https://twitter.com/lexi_lambda/status/867617563206758400">editor support</a>. The future of Hackett looks bright, and though there’s a <em>lot</em> of work left before I will be even remotely satisfied with it, I am excited and reassured that it already seems to be bearing some fruit.</p><p>So what’s left? Is Hackett ready for an initial release? Can you start writing programs in it today? Well, unfortunately, the answer is mostly <strong>no</strong>, at least if you want those programs to be at all reliable in a day or two. If everything looks so cheery, though, what’s left? What is Hackett still missing?</p><h3><a name="what-hackett-still-isn-t"></a>What Hackett still <em>isn’t</em></h3><p>I have a laundry list of features I want for Hackett. I want GADTs, indexed type families, newtype deriving, and a compiler that can target multiple backends. These things, however, are not essential. You can probably imagine writing useful software without any of them. Before I can try to tackle those, I first need to tackle some of the bits of the foundation that simply don’t exist yet (or have at least been badly neglected).</p><p>Fortunately, these things are not insurmountable, nor are they necessarily especially hard. They’re things like default class methods, static detection and prevention of orphan instances, exhaustiveness checking for pattern-matching, and a real kind system. That’s right—right now, Hackett’s type system is effectively dynamically typed, and even though you can write a higher-kinded type, there is no such thing as a “kind error”.</p><p>Other things are simply necessary quality of life improvements before Hackett can become truly usable. Type errors are currently rather atrocious, though they could certainly be worse. Additionally, typechecking currently just halts whenever it encounters a type error, and it makes no attempt to generate more than one type error at a time. Derivation of simple instances like <code>Show</code> and <code>Eq</code> is important, and it will also likely pave the way for a more general form of typeclass deriving (since it can most certainly be implemented via macros), so it’s uncharted territory that still needs to be explored.</p><p>Bits of plumbing are still exposed in places, whether it’s unexpected behavior when interoperating with Racket or errors sometimes reported in terms of internal forms. Local bindings are, if you can believe it, still entirely unimplemented, so <code>let</code> and <code>letrec</code> need to be written up. The standard library needs fleshing out, and certain bits of code need to be cleaned up and slotted into the right place.</p><p>Oh, and of course, <strong>the whole thing needs to be documented</strong>. That in and of itself is probably a pretty significant project, especially since there’s a good chance I’ll want to figure out how to best make use of Scribble for a language that’s a little bit different from Racket.</p><p>All in all, there’s a lot of work to be done! I am eager to make it happen, but I also work a full-time job, and I don’t have it in me to continue at the pace I’ve been working at for the past couple of weeks. Still, if you’re interested in the project, stay tuned and keep an eye on it—if all goes as planned, I hope to make it truly useful before too long.</p><h2><a name="answering-some-questions"></a>Answering some questions</h2><p>It’s possible that this blog post does not seem like much; after all, it’s not terribly long. However, if you’re anything like me, there’s a good chance you are interested enough to have some questions! Obviously, I cannot anticipate all your questions and answer them here in advance, but I will try my best.</p><h3><a name="can-i-try-hackett"></a>Can I try Hackett?</h3><p>Yes! With the caveat that it’s alpha software in every sense of the word: undocumented, not especially user friendly, and completely unstable. However, if you <em>do</em> want to give it a try, it isn’t difficult: just install Racket, then run <code>raco pkg install hackett</code>. Open DrRacket and write <code>#lang hackett</code> at the top of the module, then start playing around.</p><p>Also, note that the demo web server used in the example at the top of this blog post is <em>not</em> included when you install the <code>hackett</code> package. If you want to try that out, you’ll have to run <code>raco pkg install hackett-demo</code> to install the demo package as well.</p><h3><a name="are-there-any-examples-of-hackett-code"></a>Are there any examples of Hackett code?</h3><p>Unfortunately, not a lot right now, aside from the tiny examples in this blog post. However, if you are already familiar with Haskell, the syntax likely won’t be hard to pick up. Reading the Hackett source code is not especially recommended, given that it is filled with implementation details. However, if you are interested, reading the module where most of the prelude is defined isn’t so bad. You can <a href="https://github.com/lexi-lambda/hackett/blob/6ceeac05e3d2a4b2dacd39163744baf239cf65a4/hackett-lib/hackett/private/prim/base.rkt">find it on GitHub here</a>, or you can open the <code>hackett/private/prim/base</code> module on a local installation.</p><h3><a name="how-can-i-learn-more-ask-questions-about-hackett"></a>How can I learn more / ask questions about Hackett?</h3><p>Feel free to ping me and ask me questions! I may not always be able to get back to you immediately, but if you hang around, I will eventually send you a response. The best ways to contact me are via the #racket IRC channel on Freenode, the snek Slack community (<a href="http://snek.jneen.net">which you can sign up for here</a>), sending me <a href="https://twitter.com/lexi_lambda">a DM on Twitter</a>, opening <a href="https://github.com/lexi-lambda/hackett/issues">an issue on the GitHub repo</a>, or even just <a href="mailto:lexi.lambda@gmail.com">sending me an email</a> (though I’m usually a bit slower to respond to the latter).</p><h3><a name="how-can-i-help"></a>How can I help?</h3><p>Probably the easiest way to help out is to try Hackett for yourself and <a href="https://github.com/lexi-lambda/hackett/issues">report any bugs or infelicities you run into</a>. Of course, many issues right now are known, there’s just so much to do that I haven’t had the chance to clean everything up. For that reason, the most effective way to contribute is probably to pick an existing issue and try and implement it yourself, but I wouldn’t be surprised if most people found the existing implementation a little intimidating.</p><p>If you <em>are</em> interested in helping out, I’d be happy to give you some pointers and answer some questions, since it would be extremely nice to have some help. Please feel free to contact me using any of the methods mentioned in the previous section, and I’ll try and help you find something you could work on.</p><h3><a name="how-does-hackett-compare-to-x-why-doesn-t-hackett-support-y"></a>How does Hackett compare to <em>X</em> / why doesn’t Hackett support <em>Y</em>?</h3><p>These tend to be complex questions, and I don’t always have comprehensive answers for them, especially since the language is evolving so quickly. Still, if you want to ask me about this, feel free to just send the question to me directly. In my experience, it’s usually better to have a conversation about this sort of thing rather than just answering in one big comparison, since there’s usually a fair amount of nuance.</p><h3><a name="when-will-hackett-be-ready-for-me-to-use"></a>When will Hackett be ready for me to use?</h3><p>I don’t know.</p><p>Obviously, there is a lot left to implement, that is certainly true, but there’s more to it than that. If all goes well, I don’t see any reason why Hackett can’t be early beta quality by the end of this year, even if it doesn’t support all of the goodies necessary to achieve perfection (which, of course, it never really can).</p><p>However, there are other things to consider, too. The Racket package system is currently flawed in ways that make rapidly iterating on Hackett hard, since it is extremely difficult (if not impossible) to make backwards-incompatible changes without potentially breaking someone’s program (even if they don’t update anything about their dependencies)! This is a solvable problem, but it would take some work modifying various elements of the package system and build tools, so that might need to get done before I can recommend Hackett in good faith.</p><h2><a name="appendix"></a>Appendix</h2><p>It would be unfair not to mention all the people that have made Hackett possible. I cannot list them all here, but I want to give special thanks to <a href="http://www.ccs.neu.edu/home/stchang/">Stephen Chang</a>, <a href="http://www.cs.ubc.ca/~joshdunf/">Joshua Dunfield</a>, <a href="http://eecs.northwestern.edu/~robby/">Robby Findler</a>, <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a>, <a href="http://functorial.com">Phil Freeman</a>, <a href="http://www.ccs.neu.edu/home/types/">Ben Greenman</a>, <a href="https://github.com/AlexKnauth">Alex Knauth</a>, <a href="http://www.cl.cam.ac.uk/~nk480/">Neelakantan Krishnaswami</a>, and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a>. I’d also like to thank everyone involved in the Racket and Haskell projects as a whole, as well as everyone who has expressed interest and encouragement about what I’ve been working on.</p><p>As a final point, just for fun, I thought I’d keep track of all the albums I’ve been listening to while working on Hackett, just in the past few weeks. It is <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/#whats-in-a-name">on theme with the name</a>, after all. This list is not completely exhaustive, as I’m sure some slipped through the cracks, but you can thank the following artists for helping me power through a few of the hills in Hackett’s implementation:</p><ul><li><p>The Beach Boys — Pet Sounds</p></li><li><p>Boards of Canada — Music Has The Right To Children, Geogaddi</p></li><li><p>Bruce Springsteen — Born to Run</p></li><li><p>King Crimson — In the Court of the Crimson King, Larks’ Tongues in Aspic, Starless and Bible Black, Red, Discipline</p></li><li><p>Genesis — Nursery Cryme, Foxtrot, Selling England by the Pound, The Lamb Lies Down on Broadway, A Trick of the Tail</p></li><li><p>Mahavishnu Orchestra — Birds of Fire</p></li><li><p>Metric — Fantasies, Synthetica, Pagans in Vegas</p></li><li><p>Muse — Origin of Symmetry, Absolution, The Resistance</p></li><li><p>Peter Gabriel — Peter Gabriel I, II, III, IV / Security, Us, Up</p></li><li><p>Pink Floyd — Wish You Were Here</p></li><li><p>Supertramp — Breakfast In America</p></li><li><p>The Protomen — The Protomen, Act II: The Father of Death</p></li><li><p>Talking Heads — Talking Heads: 77, More Songs About Buildings and Food, Fear of Music, Remain in Light</p></li><li><p>Yes — Fragile, Relayer, Going For The One</p></li></ul><p>And of course, <em>Voyage of the Acolyte</em>, by <strong>Steve Hackett</strong>.</p><ol class="footnotes"></ol></article>Rascal is now Hackett, plus some answers to questionshttps://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/https://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/05 Jan 2017<article><p>Since I published <a href="/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/">my blog post introducing Rascal</a>, I’ve gotten some <em>amazing</em> feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that <a href="http://www.rascal-mpl.org">Rascal is a language that already exists</a>. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, <a href="https://github.com/lexi-lambda/hackett"><strong>Rascal is now known as Hackett</strong></a>.</p><p>With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.</p><h2><a name="what-s-in-a-name"></a>What’s in a name?</h2><p>First, a little trivia.</p><p>I’ve already mentioned that the old “Rascal” name was based on the names “Racket” and “Haskell”, which is true. However, it had a slightly deeper meaning, too: the name fit a tradition of naming languages in the Scheme family after somewhat nefarious things, such as “Gambit”, “Guile”, “Larceny”, and “Racket” itself. The name goes back a little bit further to the Planner programming language; Scheme was originally called Schemer, but it was (no joke) shorted due to filename length restrictions.</p><p>Still, my language isn’t really a Scheme, so the weak connection wasn’t terribly relevant. Curious readers might be wondering if there’s any deeper meaning to the name “Hackett” than a mixture of the two language names. In fact, there is. Hackett is affectionately named after the <a href="https://en.wikipedia.org/wiki/Steve_Hackett">Genesis progressive rock guitarist, Steve Hackett</a>, one of my favorite musicians. The fact that the name is a homophone with “hack-it” is another convenient coincidence.</p><p>Perhaps not the most interesting thing in this blog post, but there it is.</p><h2><a name="why-racket-why-not-haskell"></a>Why Racket? Why <em>not</em> Haskell?</h2><p>One of the most common questions I received is why I used Racket as the implementation language instead of Haskell. This is a decent question, and I think it likely stems at least in part from an item of common confusion: <strong>Racket is actually two things, a programming language and a programming language platform</strong>. The fact that the two things have the same name is probably not ideal, but it’s what we’ve got.</p><p>Racket-the-language is obviously the primary language used on the Racket platform, but there’s actually surprisingly little need for that to be the case; it’s simply the language that is worked on the most. Much of the Racket tooling, including the compiler, macroexpander, and IDE, are actually totally language agnostic. If someone came along and wrote a language that got more popular than <code>#lang racket</code>, then there wouldn’t really be anything hardcoded into any existing tooling that would give the impression that <code>#lang racket</code> was ever the more “dominant” language, aside from the name.</p><p>For this reason, Racket is ideal for implementing new programming languages, moreso than pretty much any other platform out there. The talk I linked to in the previous blog post, <a href="https://www.youtube.com/watch?v=TfehOLha-18">Languages in an Afternoon</a>, describes this unique capability. It’s short, only ~15 minutes, but if you’re not into videos, I can try and explain why Racket is so brilliant for this sort of thing.</p><p>By leveraging the Racket platform instead of implementing my language from scratch, I get the following things pretty much for free:</p><ol><li><p>I get a JIT compiler for my code, and I don’t have to implement a compiler myself.</p></li><li><p>I also get a package manager that can cooperate with Hackett code to deliver Hackett modules.</p></li><li><p>I get a documentation system that is fully indexed and automatically locally installed when you install Hackett or any package written in Hackett, and that documentation is automatically integrated with the editor.</p></li><li><p>The DrRacket IDE can be used out of the box with Hackett code, it automatically does syntax highlighting and indenting, and it even provides interactive tools for inspecting bindings (something that I demo in my aforementioned talk).</p></li><li><p>If you don’t want to use DrRacket, you can use the <a href="https://github.com/greghendershott/racket-mode">racket-mode</a> major mode for Emacs, which uses the same sets of tools that DrRacket uses under the hood, so you get most of the same DrRacket goodies without sacrificing Emacs’s power of customization.</p></li></ol><p>Reimplementing all of that in another language would take years of work, and I haven’t even mentioned Racket’s module system and macroexpander, which are the underpinnings of Hackett. GHC’s typechecker is likely roughly as complex as Racket’s macroexpander combined with its module system, but I am not currently implementing GHC’s typechecker, since I do not need all of OutsideIn(X)’s features, just Haskell 98 + some extensions.</p><p>In contrast, I truly do need all of the Racket macroexpander to implement Hackett, since the <em>Type Systems as Macros</em> paper uses pretty much every trick the Racket macro system has to offer to implement typechecking as macroexpansion. For those reasons, implementing the Racket macroexpander <strong>alone</strong> in Haskell would likely be monumentally more work than implementing a Hindley-Milner typechecker in Racket, so it doesn’t really make sense to use Haskell for that job.</p><h3><a name="actually-running-hackett-code"></a>Actually running Hackett code</h3><p>Now, it’s worth noting that GHC is much more efficient as a compiler than Racket is, for a whole host of reasons. However, since typechecking and macroexpansion are inherently strictly compile-time phases, it turns out to be totally feasible to run the typechecker/macroexpander in Racket (since in Hackett, the two things are one and the same), then compile the resulting fully-expanded, well-typed code to GHC Core. That could then be handed off to GHC itself and compiled using the full power of the GHC optimizer and compiler toolchain.</p><p>This would be no small amount of work, but it seems theoretically possible, so eventually it’s something I’d love to look into. There are various complexities to making it work, but I think it would let me get the best of both worlds without reinventing the wheel, so it’s something I want long-term.</p><p>There’s also the question of how “native” Hackett code would be, were it compiled to GHC Core. Would Hackett code be able to use Haskell libraries, and vice versa? My guess is that the answer is “yes, with some glue”. It probably wouldn’t be possible to do it completely seamlessly, because Hackett provides type information at macroexpansion time that likely wouldn’t exist in the same form in GHC. It might be possible to do some incredibly clever bridging to be able to use Haskell libraries in Hackett almost directly, but the inverse might not be true if a library’s interface depends on macros.</p><h2><a name="how-do-template-haskell-quasiquoters-compete-with-macros"></a>How do Template Haskell quasiquoters compete with macros?</h2><p>Quasiquoters have a number of drawbacks, but the two main ones are complexity and lack of composition.</p><p>S-expressions happen to be simple, and this means s-expression macros have two lovely properties: they’re easy to write, given good libraries (Racket has <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>), and they’re easy for tools to understand. Quasiquoters force implementors to write their own parsers from raw strings of characters, which is quite a heavy burden, and it usually means those syntaxes are confusing and brittle. To give a good example, consider <a href="http://www.yesodweb.com/book/persistent#persistent_code_generation">persistent’s quasiquoters</a>: they look <em>sort of</em> like Haskell data declarations, but they’re not really, and I honestly have no idea what their actual syntax really is. It feels pretty finicky, though. In contrast, an s-expression based version of the same syntax would basically look just like the usual datatype declaration form, plus perhaps some extra goodies.</p><p>Additionally, s-expression macros <em>compose</em>, and this should probably be valued more than anything else. If you’re writing code that doesn’t compose, it’s usually a bad sign. So much of functional programming is about writing small, reusable pieces of code that can be composed together, and macros are no different. Racket’s <code>match</code>, for example, is an expression, and it contains expressions, so <code>match</code> can be nested within itself, as well as other arbitrary macros that produce expressions. Similarly, many Racket macros can be extended, which is possible due to having such uniform syntax.</p><p>Making macros “stand out” is an issue of some subjectivity, but in my experience such a fear of macros tends to stem from a familiarity with bad macro systems (which, to be fair, is almost all of them) and poor tooling. I’ve found that, in practice, most of the reasons people want to know “is this a macro??” is because macros are scary black boxes and people want to know which things to be suspicious of.</p><p>Really, though, one of the reasons macros are complicated isn’t knowing which things are macros, but it’s knowing <em>which identifiers are uses and which identifiers are bindings</em>, and things like that. Just knowing that something is a macro use doesn’t actually help at all there—the syntax won’t tell you. <a href="http://i.imgur.com/HvYee19.png">Solve that problem with tools that address the problem head on, not by making a syntax that makes macros second-class citizens.</a> One of the reasons I used the phrase “syntactic abstractions” in my previous blog post is because you specifically want them to be <strong>abstractions</strong>. If you have to think of a macro in terms of the thing it expands to then it isn’t a very watertight abstraction. You don’t think about Haskell pattern-matching in terms of what the patterns compile to, you just use them. Macros should be (and can be) just as fluid.</p><h2><a name="how-can-i-help"></a>How can I help?</h2><p>Right now, what I really need is someone who understands type system implementation. You don’t need to be up to date on what’s cutting edge—I’m not implementing anything nearly as complicated as GADTs or dependent types yet—you just need to understand how to implement Haskell 98. If you have that knowledge and you’re interested in helping, even if it just means answering some of my questions, please contact me via email, IRC (the #racket channel on Freenode is a good place for now), or Slack (I’m active in the snek Slack community, <a href="http://snek.jneen.net">which you can sign up for here</a>).</p><p>If you aren’t familiar with those things, but you’re still interested in helping out, there’s definitely plenty of work that needs doing. If you want to find somewhere you can pitch in, contacting me via any of the above means is totally fine, and I can point you in the right direction. Even if you just want to be a guinea pig, that’s useful.</p><ol class="footnotes"></ol></article>Rascal: a Haskell with more parentheseshttps://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/https://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/02 Jan 2017<article><blockquote><p><strong>Note</strong>: since the writing of this blog post, Rascal has been renamed to Hackett. You can read about why in <a href="/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/">the followup blog post</a>.</p></blockquote><p>“Hey! You got your Haskell in my Racket!”</p><p>“No, you got <em>your</em> Racket in <em>my</em> Haskell!”</p><p>Welcome to the <a href="https://github.com/lexi-lambda/hackett">Rascal</a> programming language.</p><h2><a name="why-rascal"></a>Why Rascal?</h2><p>Why yet <em>another</em> programming language? Anyone who knows me knows that I already have two programming languages that I <em>really</em> like: Haskell and Racket. Really, I think they’re both great! Each brings some things to the table that aren’t really available in any other programming language I’ve ever used.</p><p>Haskell, in many ways, is a programming language that fits my mental model of how to structure programs better than any other programming language I’ve used. Some people would vehemently disagree, and it seems that there is almost certainly some heavy subjectivity in how people think about programming. I think Haskell’s model is awesome once you get used to it, though, but this blog post is not really going to try and convince you why you should care about Haskell (though that <em>is</em> something I want to write at some point). What you <em>should</em> understand, though, is that to me, Haskell is pretty close to what I want in a programming language.</p><p>At the same time, though, Haskell has problems, and a lot of that revolves around its story for metaprogramming. “Metaprogramming” is another M word that people seem to be very afraid of, and for good reason: most metaprogramming systems are ad-hoc, unsafe, unpredictable footguns that require delicate care to use properly, and <em>even then</em> the resulting code is brittle and difficult to understand. Haskell doesn’t suffer from this problem as much as some languages, but it isn’t perfect by any means: Haskell has at least two different metaprogramming systems (generics and Template Haskell) that are designed for different tasks, but they’re both limited in scope and both tend to be pretty complicated to use.</p><p>Discussing the merits and drawbacks of Haskell’s various metaprogramming capabilities is also outside the scope of this blog post, but there’s one <em>fact</em> that I want to bring up, which is that <strong>Haskell does not provide any mechanism for adding syntactic abstractions to the language</strong>. What do I mean by this? Well, in order to understand what a “syntactic abstraction” is and why you should care about it, I want to shift gears a little and take a look at why Racket is so amazing.</p><h3><a name="a-programmable-programming-language-theory-and-practice"></a>A programmable programming language: theory and practice</h3><p>I feel confident in saying that Racket has <em>the</em> most advanced macro system in the world, and it is pretty much unparalleled in that space. There are many languages with powerful type systems, but Racket is more or less alone in many of the niches it occupies. Racket has a large number of innovations that I don’t know of in any other programming language, and a significant portion of them focus on making Racket a <a href="http://www.ccs.neu.edu/home/matthias/manifesto/">programmable programming language, a language for building languages</a>.</p><p>This lofty goal is backed up by decades of research, providing Racket with an unparalleled toolkit for creating languages that can communicate, be extended, and even cooperate with tooling to provide introspection and error diagnostics. Working in Haskell feels like carefully designing a mould that cleanly and precisely fits your domain, carefully carving, cutting, and whittling. In contrast, working with Racket feels like moulding your domain until it looks the way <em>you</em> want it to look, poking and prodding at a pliable substrate. The sheer <em>ease</em> of it all is impossible for me to convey in words, so <a href="https://twitter.com/andmkent_/status/724036694773628930">you will have to see it for yourself</a>.</p><p>All this stuff is super abstract, though. What does it mean for practical programming, and why should you care? Well, I’m not going to try and sell you if you’re extremely skeptical, but if you’re interested, <a href="https://www.youtube.com/watch?v=TfehOLha-18">I gave a talk on some of Racket’s linguistic capabilities last year called <em>Languages in an Afternoon</em></a>. If you’re curious, give it a watch, and you might find yourself (hopefully) a little impressed. If you prefer reading, well, I have some <a href="/blog/2015/12/21/adts-in-typed-racket-with-macros/">blog posts</a> on this very blog that <a href="/blog/2015/08/30/managing-application-configuration-with-envy/">demonstrate what Racket can do</a>.</p><p>The basic idea, though, is that by having a simple syntax and a powerful macro system with a formalization of lexical scope, users can effectively invent entirely new language constructs as ordinary libraries, constructs that would have to be core forms in other programming languages. For example, Racket supports pattern-matching, but it isn’t built into the compiler: it’s simply implemented in the <code>racket/match</code> module distributed with Racket. Not only is it defined in ordinary Racket code, it’s actually <em>extensible</em>, so users can add their own pattern-matching forms that cooperate with <code>match</code>.</p><p>This is the power of a macro system to produce “syntactic abstractions”, things that can transform the way a user thinks of the code they’re writing. Racket has the unique capability of making these abstractions both easy to write and watertight, so instead of being a scary tool you have to handle with extreme care, you can easily whip up a powerful, user-friendly embedded domain specific language in a matter of <em>minutes</em>, and it’ll be safe, provide error reporting for misuse, and cooperate with existing tooling pretty much out of the box.</p><h3><a name="fusing-haskell-and-racket"></a>Fusing Haskell and Racket</h3><p>So, let’s assume that we <em>do</em> want Haskell’s strong type system and that we <em>also</em> want a powerful metaprogramming model that permits syntactic extensions. What would that look like? Well, one way we could do it is to put one in front of the other: macro expansion is, by nature, a compile-time pass, so we could stick a macroexpander in front of the typechecker. This leads to a simple technique: first, macroexpand the program to erase the macros, then typecheck it and erase the types, then send the resulting code off to be compiled. This technique has the following properties:</p><ol><li><p>First of all, <strong>it’s easy to implement</strong>. Racket’s macroexpander, while complex, is well-documented in academic literature and works extremely well in practice. In fact, this strategy has already been implemented! Typed Racket, the gradually-typed sister language of Racket, expands every program before typechecking. It would be possible to effectively create a “Lisp-flavored Haskell” by using this technique, and it might not even be that hard.</p></li><li><p>Unfortunately, there’s a huge problem with this approach: <strong>type information is not available at macroexpansion time</strong>. This is the real dealbreaker with the “expand, then typecheck” model, since static type information is some of the most useful information possibly available to a macro writer. In an ideal world, macros should not only have access to type information, they should be able to manipulate it and metaprogram the typechecker as necessary, but if macroexpansion is a separate phase from typechecking, then that information simply doesn’t exist yet.</p></li></ol><p>For me, the second option is unacceptable. I am <em>not</em> satisfied by a “Lisp-flavored Haskell”; I want my types and macros to be able to cooperate and communicate with each other. The trouble, though, is that solving that problem is really, really hard! For a couple years now, I’ve been wishing this ideal language existed, but I’ve had no idea how to make it actually work. Template Haskell implements a highly restricted system of interweaving typechecking and splice evaluation, but it effectively does it by running the typechecker and the splice expander alternately, splitting the source into chunks and typechecking them one at a time. This works okay for Template Haskell, but for the more powerful macro system I am looking for, it wouldn’t scale.</p><p>There’s something a little bit curious, though, about the problem as I just described it. The processes of “macroexpanding the program to erase the macros” and “typechecking the program to erase the types” sound awfully similar. It seems like maybe these are two sides of the same coin, and it would be wonderful if we could encode one in terms of the other, effectively turning the two passes into a single, unified pass. Unfortunately, while this sounds great, I had no idea how to do this (and it didn’t help that I really had no idea how existing type systems were actually implemented).</p><p>Fortunately, last year, Stephen Chang, Alex Knauth, and Ben Greenman put together a rather exciting paper called <a href="http://www.ccs.neu.edu/home/stchang/popl2017/"><em>Type Systems as Macros</em></a>, which does precisely what I just described, and it delivers it all in a remarkably simple and elegant presentation. The idea is to “distribute” the task of typechecking over the individual forms of the language, leveraging existing macro communication facilities avaiable in the Racket macroexpander to propagate type information as macros are expanded. To me, it was exactly what I was looking for, and I almost immediately started playing with it and seeing what I could do with it.</p><p>The result is <a href="https://github.com/lexi-lambda/hackett"><em>Rascal</em></a>, a programming language built in the Racket ecosystem that attempts to implement a Haskell-like type system.</p><h2><a name="a-first-peek-at-rascal"></a>A first peek at Rascal</h2><p>Rascal is a very new programming language I’ve only been working on over the past few months. It is extremely experimental, riddled with bugs, half-baked, and may turn your computer into scrambled eggs. Still, while I might not recommend that you actually <em>use</em> it just yet, I want to try and share what it is I’m working on, since I’d bet at least a few other people will find it interesting, too.</p><p>First, let me say this up front: <strong>Rascal is probably a lot closer to Haskell than Racket</strong>. That might come as a surprise, given that Rascal has very Lisp-y syntax, it’s written in Racket, and it runs on the Racket platform, but semantically, Rascal is mostly just Haskell 98. This is important, because it may come as a surprise, given that there are so few statically typed Lisps, but there’s obviously no inherent reason that Lisps need to be dynamically typed. They just seem to have mostly evolved that way.</p><p>Taking a look at a snippet of Rascal code, it’s easy to see that the language doesn’t work quite like a traditional Lisp, though:<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><pre><code>(def+ map-every-other : (forall [a] {{a -&gt; a} -&gt; (List a) -&gt; (List a)}) + [_ nil -&gt; nil] + [_ {x :: nil} -&gt; {x :: nil}] + [f {x :: y :: ys} -&gt; {x :: (f y) :: (map-every-other f ys)}]) +</code></pre><p>This is a Lisp with all the goodies you would expect out of Haskell: static types, parametric polymorphism, automatically curried functions, algebraic datatypes, pattern-matching, infix operators, and of course, <em>typeclasses</em>. Yes, with Rascal you can have your monads in all their statically dispatched glory:</p><pre><code>(data (Maybe a) + (just a) + nothing) + +(instance (Monad Maybe) + [join (case-lambda + [(just (just x)) (just x)] + [_ nothing])]) +</code></pre><p>So far, though, this really <em>is</em> just “Haskell with parentheses”. As alluded to above, however, Rascal is a bit more than that.</p><h3><a name="core-forms-can-be-implemented-as-derived-concepts"></a>Core forms can be implemented as derived concepts</h3><p>Rascal’s type system is currently very simple, being nothing more than Hindley-Milner plus ad-hoc polymorphism in the form of typeclasses. Something interesting to note about it is that it does not implement ADTs or pattern-matching anywhere in the core! In fact, ADTs are defined as two macros <code>data</code> and <code>case</code>, in an entirely separate module, which can be imported just like any other library.</p><p>The main <code>rascal</code> language provides ADTs by default, of course, but it would be perfectly possible to produce a <code>rascal/kernel</code> language which does not include them at all. In this particular case, it seems unlikely that Rascal programmers would want their own implementation of ADTs, but it’s an interesting proof of concept, and it hints at other “core” features that could be implemented using macros.</p><p>Simple syntactic transformations are, of course, trivially defined as macros. Haskell <code>do</code> notation is defined as <a href="https://github.com/lexi-lambda/hackett/blob/87d001a82c86fb66544d25c37ffba9be1ac63464/rascal-lib/rascal/monad.rkt#L48-L58">an eleven-line macro in <code>rascal/monad</code></a>, and GHC’s useful <code>LambdaCase</code> extension is also possible to implement without modifying Rascal at all. This is useful, because there are many syntactic shorthands that are extremely useful to implement, but don’t make any sense to be in GHC because they are specific to certain libraries or applications. Racket’s macro system makes those not only possible, but actually pretty easy.</p><p>While the extent of what is possible to implement as derived forms remains to be seen, many useful GHC features seem quite possible to implement without touching the core language, including things like <code>GeneralizedNewtypeDeriving</code> and other generic deriving mechanisms like <code>GHC.Generics</code>, <code>DeriveGeneric</code>, and <code>DeriveAnyClass</code>.</p><h3><a name="the-language-is-not-enough"></a>The language is not enough</h3><p>No language is perfect. Most people would agree with this, but I would take it a step further: no language is even sufficient! This makes a lot of sense, given that general-purpose programming languages are designed to do <em>everything</em>, and it’s impossible to do everything well.</p><p>Haskell programmers know this, and they happily endorse the creation of embedded domain specific languages. These are fantastic, and we need more of them. Things like <a href="http://hackage.haskell.org/package/servant">servant</a> let me write a third of the code I might otherwise need to, and the most readable code is the code you didn’t have to write in the first place. DSLs are good.</p><p>Unfortunately, building DSLs is traditionally difficult, largely in part because building embedded DSLs means figuring out a way to encode your domain into your host language of choice. Sometimes, your domain simply does not elegantly map to your host language’s syntax or semantics, and you have to come up with a compromise. This is easy to see with servant, which, while it does a remarkably good job, still has to resort to some very clever type magic to create some semblance of an API description in Haskell types:</p><pre><code>type UserAPI = "users" :&gt; Get '[JSON] [User] + :&lt;|&gt; "users" :&gt; ReqBody '[JSON] User :&gt; Post '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; Get '[JSON] User + :&lt;|&gt; "users" :&gt; Capture "userid" Integer + :&gt; ReqBody '[JSON] User + :&gt; Put '[JSON] User +</code></pre><p>The above code is <em>remarkably</em> readable for what it is, but what if we didn’t have to worry about working within the constraints of Haskell’s syntax? What if we could design a syntax that was truly the best for the job? Perhaps we would come up with something like this:</p><pre><code>(define-api User-API + #:content-types [JSON] + [GET "users" =&gt; (List User)] + [POST "users" =&gt; User -&gt; User] + [GET "users" [userid : Integer] =&gt; User] + [PUT "users" [userid : Integer] =&gt; User -&gt; User]) +</code></pre><p>This would be extremely easy to write with Racket’s macro-writing utilities, and it could even be made extensible. This could also avoid having to do the complicated typeclass trickery servant has to perform to then generate code from the above specification, since it would be much easier to just generate the necessary code directly (which still maintaining type safety).</p><p>In addition to the type-level hacks that Haskell programmers often have to pull in order to make these kinds of fancy DSLs work, free monads tend to be used to create domain-specific languages. This works okay for some DSLs, but remember that when you use a free monad, you are effectively writing a <em>runtime interpreter</em> for your language! Macros, on the other hand, are compiled, and you get ability to <em>compile</em> your DSL to code that can be optimized by all the existing facilities of the compiler toolchain.</p><h2><a name="rascal-is-embryonic"></a>Rascal is embryonic</h2><p>I’m pretty excited about Rascal. I think that it could have the potential to do some pretty interesting things, and I have some ideas in my head for how having macros in a Haskell-like language could change things. I also think that, based on what I’ve seen so far, having both macros and a Haskell-like type system could give rise to <em>completely</em> different programming paradigms than exist in either Haskell or Racket today. My gut tells me that this is a case where the whole might actually be greater than the sum of its parts.</p><p>That said, Rascal doesn’t really exist yet. Yes, <a href="https://github.com/lexi-lambda/hackett">there is a GitHub repository</a>, and it has some code in it that does… something. Unfortunately, the code is also currently extremely buggy, to the point of being borderline broken, and it’s also in such early stages that you can’t really do <em>anything</em> interesting with it, aside from some tiny toy programs.</p><p>As I have worked on Rascal, I’ve come to a somewhat unfortunate conclusion, which is that I really have almost zero interest in implementing type systems. I felt that way before I started the project, but I was hoping that maybe once I got into them, I would find them more interesting. Unfortunately, as much as I love working with powerful type systems (and really, I adore working with Haskell and using all the fancy features GHC provides), I find implementing the software that makes them tick completely dull.</p><p>Still, I’m willing to invest the time to get something that I can use. Even so, resources for practical type system implementation are scarce. I want to thank <a href="https://web.cecs.pdx.edu/~mpj/">Mark P Jones</a> for his wonderful resource <a href="https://web.cecs.pdx.edu/~mpj/thih/">Typing Haskell in Haskell</a>, without which getting to where I am now would likely have been impossible. I also want to thank <a href="http://www.stephendiehl.com">Stephen Diehl</a> for his wonderful <a href="http://dev.stephendiehl.com/fun/">Write You a Haskell</a> series, which was also wonderfully useful to study, even if it is unfinished and doesn’t cover anything beyond ML just yet.</p><p>Even with these wonderful resources, I’ve come to the realization that <strong>I probably can’t do all of this on my own</strong>. I consider myself pretty familiar with macros and macro expanders at this point, but I don’t know much about type systems (at least not their implementation), and I could absolutely use some help. So if you’re interested in Rascal and think you might be able to pitch in, please: I would appreciate even the littlest bits of help or guidance!</p><p>In the meantime, I will try to keep picking away at Rascal in the small amount of free time I currently have. Thanks, as always, to all the amazing people who have contributed to the tools I’ve been using for this project: special thanks to the authors of <em>Type Systems as Macros</em> for their help as well as the people I mentioned just above, and also to all of the people who have built Racket and Haskell and made them what they are today. Without them, Rascal would most definitely not exist.</p><ol class="footnotes"><li id="footnote-1"><p>Note that most of the Rascal code in this blog post probably doesn’t actually work on the current Rascal implementation. Pretty much all of it can be implemented in the current implementation, the syntax just isn’t quite as nice yet. <a href="#footnote-ref-1-1">↩</a></p></li></ol></article>Climbing the infinite ladder of abstractionhttps://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/https://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/11 Aug 2016<article><p>I started programming in elementary school.</p><p>When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to <a href="https://xkcd.com/974/">solve the general problem</a>. When I learned about programming, I was immediately hooked: it was <em>so easy</em> to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.</p><p>Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of <strong>abstraction</strong>.</p><h2><a name="the-brick-wall-of-inexpressiveness"></a>The brick wall of inexpressiveness</h2><p>When I started programming, I was mostly playing with ActionScript and Java, just tinkering with things and seeing what I could come up with. I had quite a lot of fun, and the joy of solving problems hooked me almost immediately, but I also ran into frustrations pretty quickly. Specifically, I started writing a lot of code that looked like this:</p><pre><code class="pygments"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="p">;</span> +<span class="p">}</span> + +<span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">setName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span> +<span class="p">}</span></code></pre><p>This is a bit of a cheap example, given that Java getters and setters are something of a programming language punching bag at this point, but I really did write them, and I really did get frustrated by them! I learned object-oriented design patterns, and I pored over books, forum threads, blog posts, and Stack Overflow questions about how to structure code to prevent spaghetti, but no matter how hard I tried, I kept having to type things that looked suspiciously similar to each other.</p><p>It was really quite frustrating, because no matter how I approached the problem, I ended up with a boilerplate-heavy mess. The <em>whole reason</em> I got started programming was to avoid this sort of thing, so what could I do? Well, it became increasingly obvious to me that Java had to go, and I needed to try something else. I started learning two very different programming languages, JavaScript and Objective-C, and I liked them both, for different reasons.</p><p>When I learned JavaScript, I discovered the closure, the first-class function, and I was entranced by it. Through jQuery, I learned of its power to design APIs that could be fun to use, dropping the boring, “heavy” feeling that Java carried around everywhere. With Objective-C, on the other hand, I learned about the power of a more dynamic object system, something with interesting syntax and the ability to handle “message passing” at a far higher level than Java ever could.</p><p>Both of these languages were flawed, as all languages are, but they opened my mind to the idea that <em>programming languages</em> could drastically influence the way I thought about problem solving, and they set me on a quest to find the programming language that would eliminate boilerplate once and for all.</p><h2><a name="discovering-lisp"></a>Discovering Lisp</h2><p>Over the next few years, I grew to appreciate JavaScript’s small, simple core, despite rather disliking its object system and poor faculties for user-friendly data modeling. I pored over its history, and I found out that its design was heavily influenced by an obscure little language called Scheme, as well as an even more obscure language called Self, and a part of me started to wonder what it would be like to incorporate those languages’ ideas without some of the compromises JavaScript had made.</p><p>This idea lingered in the back of my head for a couple years, and while I tried to play with Scheme a couple times, it was simply too inaccessible for me. I was used to languages with powerful, easy to use IDEs, and when I found myself with nothing more than a command-line executable and rather scarce documentation, I was at a loss for how to begin. Even if I could do math in the REPL, where could I go from there? I’d started programming by building games, then websites. What could I possibly do with Scheme?</p><p>The language (or rather, its lack of an ecosystem) proved too intimidating for me at that young age, but the idea of Lisp’s homoiconicity stuck with me. Eventually, I started to design my very own programming language, a <a href="https://github.com/lexi-lambda/libsol">highly dynamic Lisp with a prototypal object system called Sol</a>. I worked on it for about a year, and when I was done with it, it had a not-too-shabby complement of features: it had lambdas, macros, a fully-featured object model, and a CommonJS-esque module system, complete with the ability to dynamically import arbitrary C extensions. It was by far the largest project I’d ever worked on, and when I was done, I was pretty pleased.</p><p>Unfortunately, it was also abysmally slow.</p><p>I turned to a local college to find some people who could give me feedback and maybe point me in the right direction, and someone told me about another obscure programming language called <a href="http://racket-lang.org">Racket</a>. At about the same time, someone pointed me to a totally different language called <a href="https://www.haskell.org">Haskell</a>. This was uncharted territory for me, and for a while, I didn’t really explore either of those languages further. Eventually, though, I dove into them in earnest, and what I found has dramatically altered my perspective on programming since then.</p><h2><a name="a-journey-into-complexity"></a>A journey into complexity</h2><p>Fast forward about three years, and today, I am employed writing Haskell, and I spend most of my free time writing Racket. These languages left a mark on me, and while I’ve learned <em>so much more</em> since then, I find myself continually bucking the mainstream and coming back to functional programming, hygienic macros, and possibly the most powerful type system in existence in a production-ready programming language.</p><p>I’ve also started realizing something else, though: <strong>the languages I’ve settled into are <em>really complicated</em>.</strong></p><p>When I started programming, I thought about things like numbers, text, and shapes on a screen. Before long, I learned about functions, then classes, then message-passing and lambdas. I dove into macros and typeclasses, and now I speak in functors and monads, sets of scopes and internal definition contexts, and parser combinators and domain specific languages.</p><p>Why?</p><p>Sometimes I talk to fellow programmers, and they are horrified by the types of terms I fling around. “Why would you ever need something called a ‘monad’?” they ask, completely perplexed. “Macros are confusing,” they argue. “Being explicit is better.”</p><p>Obviously, I disagree, but why? What have I given up? If my fellow programmers cannot understand what I’m writing, is it actually worth it?</p><p>I’ve searched for years to find a programming language that will eliminate boilerplate, that will allow me to express my ideas succinctly and cleanly, that will let me turn hard problems into trivial ones, and I’ve discovered two completely different approaches to tackling those issues. Racket has macros, and Haskell has its fancy type system. Both of these things are lightyears ahead of where I was nearly a decade ago, writing dozens of lines of repetitive Java that ultimately did very little, but I’m still dealing with the same problems.</p><p>Racket knows too little about my program—it can’t figure out what I mean based on the type of thing I’m operating on because it is (mostly) dynamically typed. I <em>still</em> have to clarify myself and write things that feel redundant because the computer isn’t smart enough to figure out the “obvious”. Similarly, Haskell is too limiting—the compiler cannot deduce constraints I can solve in my head in seconds, and its syntax is not extensible like Racket’s is. Every day, I peer into piles upon piles of monadic computation, and really, what have I gained?</p><h3><a name="improvement-but-never-mastery"></a>Improvement, but never mastery</h3><p>Like almost anything in life, programming is not really a perfectable art. There’s always some unlearned skill or undiscovered technique, and part of this potential for perpetual self-improvement is one of the things that I find so attractive about the field. That said, I this it is reasonable to say that certain languages have higher ceilings than others.</p><p>For example I am pretty confident that I <em>get</em> JavaScript. The language has lots of nooks and crannies that I don’t completely understand, but I feel pretty confident that I understand its semantics well enough to be able to grasp any piece of JavaScript code without too much incredulity. Now, that’s not to say that JavaScript is a simplistic language—far from it—but most of the ways I improve my JavaScripting abilities are learning new techniques <em>within</em> the language, not entirely new linguistic constructs.</p><p>On the other hand, languages like Haskell and Racket tend to blur the line. I feel like I have a good grasp of Haskell’s core, but do I have a good intuition for laziness? Do I completely grok type families? What about <code>TypeInType</code>? Ultimately, I have to come to the conclusion that I do not fully understand Haskell, much less a lot of the advanced category theory that composes some of its most powerful libraries. Racket manages to blur the line between language and library even further, and while I consider myself a decent Racketeer, I absolutely do <em>not</em> have a good grasp on all the intricacies of Racket’s macro system.</p><p>This is especially obvious to me at work, given that I write Haskell in a team setting. Just like back when I was writing Java, I end up with solutions that don’t satisfy me, and I reach for increasingly powerful constructs to help alleviate my qualms. Sometimes, I find myself cracking out <code>DataKinds</code>, and it might even help my problem, but there’s a cost: my coworkers are sometimes confused.</p><p>Every time I climb to the next rung on the ladder of abstraction, those only a couple rungs below me (even if we’re all hundreds of rungs up!) find themselves perplexed. In the worst case, people may even blame their confusion on their own inadequacy or lack of skill. This is <em>terrible</em>, especially when I know that, by the time they’ve caught up, I’ll be off playing with some new toy: comonads or type families or classy lenses. The cycle continues, and nobody is ever truly satisfied—I always want to find a new abstraction that will make things simpler, and those just a couple steps behind me struggle to keep up.</p><p>Of course, I experience it from the opposite perspective just as often: I delve into Edward Kmett’s fancier libraries or Phil Freeman’s blog posts about category theory, and I recognize that I am rather lost. Sometimes, I find myself understanding things, but just as often, I cannot wrap my head around the concepts being discussed. I may figure them out eventually, sure, but by then everyone else has moved on to even <em>more</em> advanced things, and still, none of them truly solve my problems.</p><h2><a name="ultimately-it-all-has-at-least-a-little-value"></a>Ultimately, it all has (at least a little) value</h2><p>It would be nice to think about all that and say, well, “Let’s finally break the cycle. Let’s stop deluding ourselves into thinking our solutions to our self-made problems are actually solving anything.” It would be great if I could tell myself that, but I unfortunately really can’t.</p><p>The scariest part of all is that I think it’s completely worthwhile.</p><p>So much of these more and more complicated abstractions are trying to do the same basic thing: come up with a better way of modeling the problem. In some sense, that’s all programming really is, modeling a domain in a way that can be leveraged by a digital computer. Our increasingly complicated DSLs <em>seem</em> unnecessarily complicated, they <em>seem</em> increasingly removed from reality, but that’s only because we’re getting better at creating languages that are closer to our domains without the baggage of preconceptions that came before us.</p><p>The downside is that, without an understanding of those preconceptions, a lot of what we come up with seems like patent gibberish to those unaware of our languages’ history.</p><p>Most programmers, even those who have never seen BASIC before, can figure out what this snippet does:</p><pre><code class="pygments"><span class="nl">10</span><span class="w"> </span><span class="kr">INPUT</span><span class="w"> </span><span class="s2">"What is your name: "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span> +<span class="nl">20</span><span class="w"> </span><span class="kr">PRINT</span><span class="w"> </span><span class="s2">"Hello "</span><span class="p">;</span><span class="w"> </span><span class="vg">U$</span></code></pre><p>On the other hand, very few would probably understand this one:</p><pre><code class="pygments"><span class="c1">-- | A class for categories.</span> +<span class="c1">-- id and (.) must form a monoid.</span> +<span class="kr">class</span><span class="w"> </span><span class="kt">Category</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="c1">-- | the identity morphism</span> +<span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span> + +<span class="w"> </span><span class="c1">-- | morphism composition</span> +<span class="w"> </span><span class="p">(</span><span class="o">.</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">cat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">c</span></code></pre><p>Yet very few new programs are being written in BASIC, and lots are being written in Haskell.</p><p>Even one of the most popular, fastest-growing programming languages in the world, JavaScript, a language considered relatively accessible compared to things like Haskell, would likely be incomprehensible to a programmer not familiar with its syntax:</p><pre><code class="pygments"><span class="k">export</span><span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composeWithProps</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">curry</span><span class="p">((</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">composed</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">childProps</span><span class="w"> </span><span class="p">=&gt;</span> +<span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">parentProps</span><span class="p">,</span><span class="w"> </span><span class="nx">createElement</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">omit</span><span class="p">([</span><span class="s1">&#39;children&#39;</span><span class="p">],</span><span class="w"> </span><span class="nx">childProps</span><span class="p">),</span><span class="w"> </span><span class="nx">childProps</span><span class="p">.</span><span class="nx">children</span><span class="p">));</span> +<span class="w"> </span><span class="c1">// give the composed component a pretty display name for debugging</span> +<span class="w"> </span><span class="nx">composed</span><span class="p">.</span><span class="nx">displayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`Composed(</span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="si">}</span><span class="sb">, </span><span class="si">${</span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span><span class="si">}</span><span class="sb">)`</span><span class="p">;</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">composed</span><span class="p">;</span> +<span class="p">});</span></code></pre><p>Moving towards increasingly specialized syntaxes is not inherently bad—it can often be indicative of a more streamlined, domain-specific way of thinking—but while it may dramatically increase the productivity of a seasoned programmer, it can be nothing short of baffling to a newcomer.</p><p>That, specifically, is the crux of my fear: are we always aware of who we are optimizing for? I do not have a moral problem with writing code to optimize concision for seasoned programmers; after all, brevity is one of the primary ways code is made more readable (verbosity is the enemy of understanding). However, when that concision comes at the cost of beginners’ understanding, the picture becomes a bit more grey. It is not wrong to write things that are highly optimized for one’s own knowledge and understanding, and establishing a group of such people can make for an <em>extremely</em> productive team. It’s just also important to understand that others will likely be confused, and without being willing to invest the time and money into education, smart, diligent people will still fail to grasp the concepts, and they will likely be wholly uninterested in them.</p><h3><a name="reactionary-anti-intellectualism-and-the-search-for-moderation"></a>Reactionary anti-intellectualism and the search for moderation</h3><p>I have noticed lately that people close to my circles have started regularly slinging insults at people who work in highly specialized notation. Math, including things like category and type theory, has become an especially acceptable punching bag. <a href="https://twitter.com/lexi_lambda/status/763111451691134976">I recently tweeted a picture of some rather dense mathematics from a paper I’d read</a>, and I was frankly disturbed at some of the vitriolic responses. Academia is sometimes described as “masturbatory”, and honestly, that is both offensive and hypocritical.</p><p>Mathematical notation is not perfect, no more than dense Haskell, heavily metaprogrammed Ruby, or IIFE-packed JavaScript. Still, it serves a purpose, and sometimes spelling things out is neither practically feasible nor a theoretical improvement. Programmers would not take kindly to being asked to write all their code out as prose, nor would they like being told that using higher-order functions like <code>map</code> should be banned because they are too confusing and not immediately self-explanatory.</p><p>I am glad that people are focusing on usability and accessibility more than ever, and I think that’s one of the areas I’m the most interested in. I want to get the best of both worlds: I aim to write code in a highly concise, precise style, but I try and produce intuitive interfaces with human-readable errors upon failure. To me, a user-hostile yet technically functional library is a buggy one, and I would happily file a bug report about a confusing API or error message.</p><p>Abstraction is what seems to make programming possible, and indeed, it’s what makes most modern <em>technology</em> possible. It’s what allows people to drive a car without knowing how an internal combustion engine works, and it’s what allows people to browse the web without having a deep understanding of internet protocol. In programming, abstraction serves a similar purpose. Of course, just like all tools, abstractions can have rather different goals: the average user will not pick up Photoshop in a day, but a power user is not going to be satisfied with Paint.</p><p>Programmers are professionals, and we work in a technical domain. I am absolutely of the belief that programming, like any other field, is not always about what comes easiest: sometimes it’s important to sit down and study for a while to grok a particularly complicated concept, and other times, it’s simply important to learn by trying, failing, and asking questions. I strive to find that blend of accessible, concise, and robust, and just like everything else, that target shifts depending on the situation and people I’m working with.</p><p>I honestly don’t know if Racket and Haskell are worth their costs in complexity. At the end of the day, maybe what really matters is writing simple, consistent things that other people can understand. I really hope that there is a place for more powerful languages within a team, but there’s something to be said about which languages tend to get the most popular.</p><p>Ultimately, though, I am just trying to be aware of the tradeoffs I’m making, the benefits I’m getting, and the impact on those I’m working with. I will continue to search for abstractions that can better fit my needs, and I am sure I will keep on climbing the ladder of abstraction for years to come—I just really hope I’m not wasting my time.</p><ol class="footnotes"></ol></article>Simple, safe multimethods in Rackethttps://lexi-lambda.github.io/blog/2016/02/18/simple-safe-multimethods-in-racket/https://lexi-lambda.github.io/blog/2016/02/18/simple-safe-multimethods-in-racket/18 Feb 2016<article><p>Racket ships with <code>racket/generic</code>, a system for defining <em>generic methods</em>, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports <em>single dispatch</em>. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.</p><h2><a name="motivating-multiple-dispatch"></a>Motivating multiple dispatch</h2><p>What is multiple dispatch and why is it necessary? Well, in most cases, it <em>isn’t</em> necessary at all. <a href="http://dl.acm.org/citation.cfm?doid=1449764.1449808">It has been shown that multiple dispatch is much rarer than single dispatch in practice.</a> However, when actually needed, having multiple dispatch in the toolbox is a valuable asset.</p><p>A classic example of multiple dispatch is multiplication over both scalars and vectors. Ideally, all of the following operations should work:</p><pre><code>2 × 3 = 6 +2 × ⟨3, 4⟩ = ⟨6, 8⟩ +⟨3, 4⟩ × 2 = ⟨6, 8⟩ +</code></pre><p>In practice, most languages do not support such flexible dispatch rules without fairly complicated branching constructs to handle each permutation of input types. Furthermore, since most languages only support single dispatch (such as most object-oriented languages), it is nearly impossible to add support for a new combination of types to an existing method.</p><p>To illustrate the above, even if a language supported operator overloading <em>and</em> it included a <code>Vector</code> class that overloaded multiplication to properly work with numbers and vectors, it might not implement matrix multiplication. If a user defines a <code>Matrix</code> class, they may overload <em>its</em> multiplication to support numbers, vectors, and matrices, but it is impossible to extend the multiplication implementation for the <code>Vector</code> class. That method is now completely set in stone, unless it is edited directly (and the programmer may not have access to <code>Vector</code>’s implementation).</p><p>Multiple dispatch solves all of these problems. Rather than specify implementations of functions for singular types, it is possible to specify implementations for sets of types. In the above example, a programmer would be able to define a new function that operates on <code>Vector</code> and <code>Matrix</code> arguments. Since each definition does not “belong” to any given type, extending this set of operations is trivial.</p><h2><a name="multiple-dispatch-in-racket"></a>Multiple dispatch in Racket</h2><p>This blog post is somewhat long and technical, so before proceeding any further, I want to show some real code that actually works so you can get a feel for what I’m talking about. As a proof-of-concept, I have created <a href="https://github.com/lexi-lambda/racket-multimethod">a very simple implementation of multiple dispatch in Racket</a>. The above example would look like this in Racket using my module:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Pardon the somewhat clunky syntax, but the functionality is there. Using the above code works as expected:</p><pre><code>&gt; (mul (num 2) (num 3)) +(num 6) +&gt; (mul (num 2) (vec '(3 4))) +(vec '(6 8)) +&gt; (mul (vec '(3 4)) (num 2)) +(vec '(6 8)) +</code></pre><p>Making the above snippet work is not particularly hard. In fact, it’s likely that most competent Racketeers could do it without much thought. However, there’s a tiny bit more going on behind the scenes than it may seem.</p><h2><a name="the-problem-with-multiple-dispatch"></a>The problem with multiple dispatch</h2><p>The single-dispatch design limitation of <code>racket/generic</code> comes directly from a desire to avoid what has been described as “spooky action at a distance”, a problem that is prevalent in many systems that support methods with multiple dispatch (aka <em>multimethods</em>). Specifically, the issue arises when new method implementations are defined for existing datatypes, which can have far-reaching effects throughout a program because the method table is global state. Both CLOS and Clojure suffer from this shortcoming.</p><p>Interestingly, Haskell with multi-parameter typeclasses (a nonstandard but highly useful extension) makes it quite trivial to create constructs similar to multiple dispatch (though the overload resolution is done at compile-time). The similarities are significant: Haskell <em>also</em> suffers from the possibility of a certain sort of “spooky action”. However, Haskell’s static typing and resolution allows the compiler to catch these potential issues, known as “orphan instances”, at compile time. Even though Racket does not support the same sort of static typing, the same idea can be used to keep multiple dispatch safe using the macro system.</p><h2><a name="safe-dynamically-typed-multiple-dispatch"></a>Safe, dynamically-typed multiple dispatch</h2><p>In order to make multiple dispatch safe, we first need to determine exactly what is unsafe. Haskell has rules for determining what constitutes an “orphan instance”, and these rules are equally applicable for determining dangerous multimethod implementations. Specifically, a definition can be considered unsafe if <em>both</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented was declared in a different module from the implementation.</p></li><li><p><em>All</em> of the types used for dispatch in the multimethod instance were declared in a different module from the implementation.</p></li></ol><p>Conversely, a multimethod implementation is safe if <em>either</em> of the following conditions are true:</p><ol><li><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></li><li><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></li></ol><p>Why do these two rules provide a strong enough guarantee to eliminate the dangers created by global state? Well, to understand that, we need to understand what can go wrong if these rules are ignored.</p><h3><a name="multimethods-and-dangerous-instances"></a>Multimethods and dangerous instances</h3><p>What exactly is this dangerous-sounding “spooky action”, and what causes it? Well, the trouble stems from the side-effectful nature of multimethod instance definitions. Consider the Racket module from earlier, which defines multiplication instances for scalars and vectors:</p><pre><code class="pygments"><span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mul</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">num</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct-out</span><span class="w"> </span><span class="n">vec</span><span class="p">))</span> + +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="n">val</span><span class="p">))</span> +<span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="n">vals</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-generic</span><span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">y</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">vec</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">curry</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">num-val</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">v</span><span class="p">))))</span> + +<span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="n">n</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">mul</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">v</span><span class="p">))</span></code></pre><p>Note that there is not actually a <code>(mul vec vec)</code> implementation. This is intentional: there are <em>two</em> ways to take the product of two vectors, so no default implementation is provided. However, it is possible that another module might desire an instance for <code>mul</code> that takes the dot product, and the programmer might write the following definition:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">((</span><span class="n">mul</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="n">vec</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">y</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">num</span><span class="w"> </span><span class="p">(</span><span class="nb">foldl</span><span class="w"> </span><span class="nb">+</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">vec-vals</span><span class="w"> </span><span class="n">y</span><span class="p">)))))</span></code></pre><p>However, there is something fishy about the above definition: it doesn’t need to be exported with <code>provide</code> to work! Since instances don’t create new bindings, they only add dispatch options, they don’t ever need to <code>provide</code> anything. This is problematic, though: it means that a program could continue to happily compile <em>even if</em> the module containing the dot product instance was never loaded with <code>require</code>, but an attempt to multiply two vectors would fail at runtime, claiming that there was no <code>(mul vec vec)</code> implementation. This drastic change of behavior violates Racket programmers’ assumptions about the guarantees made by modules (<code>require</code> should not cause any side-effects if the module’s bindings are not used).</p><p>Of course, while this seems potentially unexpected, it is workable: just be careful to <code>require</code> modules containing instances. Unfortunately, it gets much worse—what if a different library defines <em>its own</em> <code>(mul vec vec)</code> instance? What if that instance takes the cross product instead? That library may function entirely properly on its own, but when loaded alongside the program that defines a dot product instance, it is impossible to determine which instance should be used where. Because <code>define-instance</code> operates by modifying the aforementioned global state, the implementations clash, and the two systems <em>cannot</em> continue to operate together as written.</p><p>This is pretty bad. Defining extra instances is a reasonable use-case for multiple dispatch, but if these instances can break <em>third-party code</em>, how can they be trusted? This sort of problem can make multiple dispatch difficult to reason about and even more difficult to trust.</p><h3><a name="what-determines-safety"></a>What determines safety?</h3><p>With those problems in mind, we can turn back to the two rules for <em>safe</em> multiple dispatch. How do they prevent the above issues? Well, let’s take them one at a time.</p><p>Remember that an instance can be unequivocally determined to be safe if either of the two conditions are true, so we can consider them entirely independently. The first one is simple—an instance is safe if the following condition holds:</p><blockquote><p>The multimethod that is being implemented is declared in the same module as the implementation.</p></blockquote><p>This one is pretty obvious. It is impossible to create a “bad” instance of a method declared in the same module because it is impossible to import the method without also bringing in the instance. Furthermore, a conflicting instance cannot be defined at the place where the types themselves are defined because that would require a circular module dependency, which Racket does not permit.</p><p>With the above explanation in mind, the second condition should make sense, too:</p><blockquote><p><em>Any</em> of the types used for dispatch in the multimethod instance are declared in the same module as the implementation.</p></blockquote><p>The same argument for the first point holds for the second, but with the parties swapped. Again, it is impossible to use the instance without somehow requiring the module that defines the datatype itself, so the instance would always be required, anyway. The most interesting aspect of this condition is that it demonstrates that instances can be defined for existing datatypes (that are defined in other modules) just so long as <em>at least one</em> of the datatypes is defined in the same module. This continues to permit the important use-case of extending the interfaces of existing types.</p><h3><a name="encoding-the-safety-rules-into-racket-s-macro-system"></a>Encoding the safety rules into Racket’s macro system</h3><p>In order to keep track of which methods and instances are defined where, I leveraged a technique based on the one <a href="http://www.ccs.neu.edu/racket/pubs/scheme2007-ctf.pdf">used by Typed Racket to keep track of whether or not a typed identifier is used in a typed or untyped context</a>. However, instead of using a simple mutable boolean flag, I used a mutable <a href="http://docs.racket-lang.org/syntax/syntax-helpers.html#%28tech._identifier._set%29">free identifier set</a>, which keeps track of the identifiers within a given module that should be considered “privileged”.</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">syntax/id-set</span><span class="p">)</span> + +<span class="p">(</span><span class="k">provide</span><span class="w"> </span><span class="n">mark-id-as-privileged!</span> +<span class="w"> </span><span class="n">id-privileged?</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="p">(</span><span class="n">mutable-free-id-set</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-add!</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">free-id-set-member?</span><span class="w"> </span><span class="n">privileged-ids</span><span class="w"> </span><span class="n">id</span><span class="p">))</span></code></pre><p>Making this work with <code>define-generic</code> is obvious: just invoke <code>mark-id-as-privileged!</code> on the method name to note that the method is “privileged” in the scope of the current module. Keeping track of privileged structs is similarly straightforward, though it is a little more devious: the <code>multimethod</code> module provides a custom <code>struct</code> macro that just expands to <code>struct</code> from <code>racket/base</code>, but adds privilege information.</p><p>The <code>define-instance</code> macro does all the heavy lifting to ensure that only privileged identifiers can be used in instance definitions. A simple check for the identifier annotations is performed before proceeding with macro expansion:</p><pre><code class="pygments"><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">types</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">types</span><span class="p">)))</span></code></pre><p>When the privilege checks fail, an error is raised:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">)))</span></code></pre><p>With the above safeguards in place, the dangerous dot product implementation from above <strong>would not be allowed</strong>. The checks manage to encode both of the safety rules into the macro system such that invalid instances will fail <em>at compile time</em>, preventing dangerous uses of multimethods from ever slipping by unnoticed.</p><h3><a name="actually-implementing-multiple-dispatch"></a>Actually implementing multiple dispatch</h3><p>The rest of the multimethod implementation is relatively straightforward and is not even particularly robust. If anything, it is the bare minimum of what would be needed to allow the safety mechanisms above to work. Lots of features that would likely be needed in a real implementation are not included, and graceful error handling is largely ignored.</p><p>Multimethods themselves are implemented as Racket <a href="http://docs.racket-lang.org/guide/proc-macros.html#%28tech._transformer._binding%29">transformer bindings</a> containing custom data, including a reference to the multimethod’s arity and dispatch table. The custom datatype includes a <code>prop:procedure</code> structure type property, which allows such bindings to also function as macros. The macro procedure expands to an operation that looks up the proper instance to use in the multimethod’s dispatch table and invokes it with the supplied arguments.</p><p>The relevant code for defining multimethods is reproduced below:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="n">arity</span><span class="w"> </span><span class="n">dispatch-table</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:property</span><span class="w"> </span><span class="nb">prop:procedure</span> +<span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">stx</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parse</span><span class="w"> </span><span class="n">stx</span> +<span class="w"> </span><span class="p">[(</span><span class="n">method</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">arg</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">method</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="p">(</span><span class="n">apply-multimethod</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="n">args</span><span class="p">))]))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-generic</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method:id</span><span class="w"> </span><span class="n">arg:id</span><span class="w"> </span><span class="n">...+</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">arity</span><span class="w"> </span><span class="p">(</span><span class="nb">length</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">arg</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">generate-temporary</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="n">mark-id-as-privileged!</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="nb">make-hash</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod</span><span class="w"> </span><span class="n">arity</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">dispatch-table</span><span class="p">))))]))</span></code></pre><p>The dispatch tables are implemented entirely in terms of Racket’s structure types, so while they can be defined on arbitrary structure types (including ones defined in the Racket standard library), they <em>cannot</em> be defined on primitives such as pairs or vectors. Implementations are registered in the dispatch table using the compile-time information associated with structs’ transformer bindings, and the same information is retrieved from struct instances at runtime to look up the proper implementation to call. Notably, this only works if the struct is <code>#:transparent</code>, or more generally and accurately, if the calling code has access to the struct’s inspector. All structs defined by the <code>struct</code> form from the <code>multimethod</code> module are automatically marked as <code>#:transparent</code>.</p><p>The following code implements defining multimethod instances:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="n">id</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">raise-syntax-error</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">define-instance</span> +<span class="w"> </span><span class="s2">"expected name of struct defined in current module"</span> +<span class="w"> </span><span class="n">id</span><span class="p">))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-instance</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="c1">; standard (define (proc ...) ...) shorthand</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">((</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="n">body:expr</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-instance</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="k">λ</span><span class="w"> </span><span class="n">args</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="k">...</span><span class="p">))]</span> +<span class="w"> </span><span class="c1">; full (define proc lambda-expr) notation</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="p">(</span><span class="n">method</span><span class="w"> </span><span class="n">type:id</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span><span class="w"> </span><span class="n">proc:expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">let*</span><span class="w"> </span><span class="p">([</span><span class="n">multimethod</span><span class="w"> </span><span class="p">(</span><span class="nb">syntax-local-value</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[</span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">id-privileged?</span><span class="w"> </span><span class="o">#&#39;</span><span class="n">method</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="k">unless</span><span class="w"> </span><span class="p">(</span><span class="k">or</span><span class="w"> </span><span class="n">privileged?</span><span class="w"> </span><span class="p">(</span><span class="nb">ormap</span><span class="w"> </span><span class="n">id-privileged?</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">)))</span> +<span class="w"> </span><span class="p">(</span><span class="n">assert-privileged-struct!</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))))</span> +<span class="w"> </span><span class="p">(</span><span class="k">with-syntax</span><span class="w"> </span><span class="p">([</span><span class="n">dispatch-table</span><span class="w"> </span><span class="p">(</span><span class="n">multimethod-dispatch-table</span><span class="w"> </span><span class="n">multimethod</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="p">(</span><span class="nb">compose1</span><span class="w"> </span><span class="nb">first</span><span class="w"> </span><span class="n">extract-struct-info</span><span class="w"> </span><span class="nb">syntax-local-value</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">attribute</span><span class="w"> </span><span class="n">type</span><span class="p">))])</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">([</span><span class="n">struct-types</span><span class="w"> </span><span class="p">(</span><span class="nb">list</span><span class="w"> </span><span class="n">struct-type-id</span><span class="w"> </span><span class="k">...</span><span class="p">)])</span> +<span class="w"> </span><span class="p">(</span><span class="nb">hash-set!</span><span class="w"> </span><span class="n">dispatch-table</span><span class="w"> </span><span class="n">struct-types</span><span class="w"> </span><span class="n">proc</span><span class="p">))))]))</span></code></pre><p>The resulting implementation is a useful, if certainly incomplete implementation of multimethods in Racket that does not sacrifice the safety provided by <code>racket/generic</code>’s single-dispatch approach.</p><h2><a name="related-work-advantages-and-disadvantages-and-areas-for-future-improvement"></a>Related work, advantages and disadvantages, and areas for future improvement</h2><p>As previously mentioned, this implementation of multiple dispatch was inspired by the types of APIs offered by CLOS and Clojure while also maintaining the safety of <code>racket/generic</code>. The inspiration for the safety rules came from GHC’s detection of orphan instances. Although most of the ideas presented above exist in other places, I am unsure if the concept of safety checking has been used before in any dynamically-typed programming languages.</p><p>The primary advantage offered over Racket’s existing generics system is obvious: multiple dispatch. Furthermore, this system can supersede many uses of <code>racket/generic</code> simply by dispatching on a single type. However, the current implementation does <em>not</em> support all of the features of <code>racket/generic</code>, such as supporting non-structure types and allowing fallback implementations. While those are well within the realm of possibility, other things like attaching structure type properties are probably not possible with this approach, so it is unlikely that the existing system could be subsumed by one like this one.</p><p>Additionally, this implementation would almost certainly need numerous improvements before being useful to most programmers:</p><ul><li><p><strong>Good error reporting for failure cases.</strong> Right now, even something obvious like calling a method on values that do not implement it simply fails with an error produced by <code>hash-ref</code>. In a more interesting sense, using the arity to generate compile-time error messages for <code>define-instance</code> would be a nice improvement.</p></li><li><p><strong>Support for Racket primitive data types.</strong> This might require some cooperation from Racket itself to permit an elegant implementation, but they could also just be special-cased. So long as lookup for primitives was done <em>after</em> consulting the main dispatch table, there wouldn’t be any performance hit for non-primitive types.</p></li><li><p><strong>Option to supply fallback implementations.</strong> This wouldn’t be too hard at all, though it’s questionable whether or not it would be useful without method groupings like <code>define/generic</code> provides. There would likely also need to be some sort of way to check if a set of values implements a particular method.</p></li><li><p><strong>Better cooperation with structure inspectors to alleviate the need for all structures to be transparent.</strong> It’s currently unclear to me how exactly this works and how it <em>should</em> work. There might be a better way to do this without mucking with inspectors.</p></li><li><p><strong>Much more flexible argument lists, including the ability to specify arguments that are not used for dispatch.</strong> This is really a pretty fundamental requirement, but the parsing required was significant enough for me to put it off for this initial prototype.</p></li><li><p><strong>Scribble forms to document generic methods and their instances.</strong> This is something <code>racket/generic</code> <em>doesn’t</em> have, and it has suffered for it. It would be very nice to have easy documentation forms for multimethods.</p></li><li><p><strong>Proper consideration of struct subtyping.</strong> Racket structs support subtyping, which I have not given much thought for this prototype. It is possible that subtyping violates constraints I had assumed would hold, so reviewing the existing code with that context would be useful.</p></li></ul><p>I’m not sure how much effort is involved in most of the above ideas, and in fact I’m not even completely sure how useful this system is to begin with. I have not found myself reaching much for multiple dispatch in my time as a Racket programmer, but that could simply be because it was previously unavailable. It will be interesting to see if that changes now that I have built this system, even if it is a bit rough around the edges.</p><h2><a name="conclusion"></a>Conclusion</h2><p>Despite the lack of need for multiple dispatch to solve most problems, as indicated by its general lack of support in mainstream programming languages, it’s a nice tool to have in the toolbox, and it <em>is</em> asked for in the Racket community from time to time (perhaps due to its familiarity in other parts of the Lisp world). Time will tell if pointing people to something like this will create or stifle interest in multiple dispatch for Racket.</p><p>The source for the <a href="https://github.com/lexi-lambda/racket-multimethod"><code>multimethod</code> package can be found here</a> if you are at all interested in playing with it yourself.</p><ol class="footnotes"></ol></article>ADTs in Typed Racket with macroshttps://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/https://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/21 Dec 2015<article><p>Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate <em>why</em> macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is <strong>ADTs</strong>.</p><h2><a name="warning-this-is-not-a-macro-tutorial"></a>Warning: this is not a macro tutorial</h2><p>First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you <em>are</em> interested in learning about macros, I must recommend Greg Hendershott's <a href="http://www.greghendershott.com/fear-of-macros/">Fear of Macros</a>. It is good. This is not that.</p><p>Now, with that out of the way, let's get started.</p><h2><a name="what-we-re-building"></a>What we&rsquo;re building</h2><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data types</a>, or <em>ADTs</em>, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.</p><p>Racket also already has a facility for creating custom data structures in the form of <em>structs</em>, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.</p><p>With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="kt">Tree</span></code></pre><p>This already demonstrates a few of the core things we'll need to build:</p><ol><li><p>Each ADT has a <em>data type</em>, in this case <code>Tree</code>. This name only exists in the world of types, it isn't a value.</p></li><li><p>Each ADT has various <em>data constructors</em>, in this case <code>Leaf</code> and <code>Node</code>.</p></li><li><p>Each data constructor may accept any number of arguments, each of which have a specific type.</p></li><li><p>The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.</p></li></ol><p>Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>With this in mind, we can add a fifth and final point to our list:</p><ol start="5"><li><p>ADTs must be able to be parametrically polymorphic.</p></li></ol><p>That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.</p><h3><a name="describing-adts-in-racket"></a>Describing ADTs in Racket</h3><p>How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>This looks pretty good. Just like with the Haskell implementation, <code>Tree</code> should only exist at the type level, and <code>Empty</code>, <code>Leaf</code>, and <code>Node</code> should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be <code>(Leaf 7)</code>.</p><p>Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's <code>match</code>[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">tree-sum</span><span class="w"> </span><span class="p">((</span><span class="n">Tree</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">tree</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">tree</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Node</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">l</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">r</span><span class="p">))]))</span></code></pre><p>Given that Racket's <code>struct</code> form automatically produces identifiers that cooperate with <code>match</code>, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.</p><h2><a name="implementing-adts-as-syntax"></a>Implementing ADTs as syntax</h2><p>Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. The <code>syntax/parse</code> library works like the traditional Scheme <code>syntax-case</code> on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.</p><p>Since this is not a macro tutorial, the following implementation assumes you already know how to use <code>syntax/parse</code>. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.</p><h3><a name="parsing-types-with-a-syntax-class"></a>Parsing types with a syntax class</h3><p>To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like <code>Tree</code>, or they can be identifiers with parameters, like <code>(Tree a)</code>. We'll want to cover both cases.</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">))))</span></code></pre><p>This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (<code>...+</code>) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use <code>#:attr</code> to give the <code>param</code> attribute the default value of an empty list, so this syntax class will actually <em>normalize</em> the input we get in addition to actually parsing it.</p><h3><a name="a-first-attempt-at-define-datatype"></a>A first attempt at <code>define-datatype</code></h3><p>Now we can move on to actually implementing <code>define-datatype</code>. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using <code>syntax-parser</code>, which actually does the parsing for our macro.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">]))</span></code></pre><p>This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with <code>_</code> (which will just be the identifier <code>define-datatype</code>), then expecting a <code>type-name</code>, which uses the <code>type</code> syntax class we defined above. Next, we expect zero or more <code>data-constructor</code>s, which also use the <code>type</code> syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.</p><p>Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">([</span><span class="n">f</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))]))</span></code></pre><p>This is actually really close to being correct. This will generate a struct definition for each <code>data-constructor</code>, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have <em>names</em>, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for <em>all</em> the fields, <code>f</code>, so if any data constructor has two or more fields, we'll get an error.</p><p>Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called <code>generate-temporary</code>, which generates random identifiers. Our next attempt might look like this:</p><pre><code class="pygments"><span class="o">#`</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="o">#,</span><span class="p">(</span><span class="n">generate-temporary</span><span class="p">)</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>The <code>#,</code> lets us "escape" from the template to execute <code>(generate-temporary)</code> and interpolate its result into the syntax. Unfortunately, this doesn't work. We <em>do</em> generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.</p><h3><a name="more-leveraging-syntax-classes"></a>More leveraging syntax classes</h3><p>As it turns out, this is <em>also</em> easy to do with syntax classes. We can add an extra attribute to our <code>type</code> syntax class to generate a random identifier with each one. Again, we can use <code>#:attr</code> to do that automatically. Our new definition for <code>type</code> will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span></code></pre><p>Here we're using <code>generate-temporaries</code> instead of <code>generate-temporary</code>, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each <code>param</code>.</p><p>We can now fix our macro to use this <code>field-id</code> attribute instead of the static field name:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><h3><a name="creating-the-supertype"></a>Creating the supertype</h3><p>We're almost done—now we just need to implement our overall type, the one defined by <code>type-name</code>. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">Tree</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">Leaf</span><span class="w"> </span><span class="n">Node</span><span class="p">))</span></code></pre><p>However, a polymorphic type alias would need to include the type parameters in each subtype, like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using <strong>procedural macros</strong>.</p><p>To build each properly-instantiated type, we'll use a combination of <code>define/with-syntax</code> and Racket's list comprehensions, <code>for/list</code>. The <code>define/with-syntax</code> form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by <code>syntax-parser</code>. This will allow us to break up our result into multiple steps. Technically, <code>define/with-syntax</code> is not strictly necessary—we could just use <code>#`</code> and <code>#,</code>—but it's cleaner to work with.</p><p>We'll start by defining a set of instantiated data constructor types, one per <code>data-constructor</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span></code></pre><p>Now with our definition for <code>data-type</code>, we can implement our type alias for the supertype extremely easily:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><h3><a name="putting-it-all-together"></a>Putting it all together</h3><p>There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by <code>type-name</code> are in scope for each data constructor's structure definition. We can do this by making use of <code>type-name.param</code> within each produced struct definition, resulting in this:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><p>And we're done! The final macro, now completed, looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span> + +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span><span class="w"> </span><span class="k">...</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">)))]))</span></code></pre><p>It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.</p><h2><a name="using-our-adts"></a>Using our ADTs</h2><p>With the macro built, we can now actually use our ADTs using the syntax we described! The following is now <em>valid code</em>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span> +<span class="nb">-</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">Positive-Byte</span><span class="p">)</span> +<span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span></code></pre><p>We can use this to define common data types, such as Haskell's <code>Maybe</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Nothing</span><span class="p">)</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-default</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-default</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-then</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-then</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Nothing</span><span class="p">)]))</span></code></pre><p>And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="n">Expr</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="n">Number</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Expr</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Number</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">e</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Value</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Add</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">-</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Divide</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">/</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">7</span><span class="p">))))</span> +<span class="mi">4</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, <a href="https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e">I've put together a gist here</a>.</p><h2><a name="conclusions-and-credit"></a>Conclusions and credit</h2><p>This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly <em>are</em> powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.</p><p>This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.</p><p>That said, I think it's pretty cool.</p><p>Finally, I must give credit where credit is due. Thanks to <a href="http://andmkent.com">Andrew M. Kent</a> for the creation of the <a href="https://github.com/andmkent/datatype">datatype</a> package, which served as the inspiration for this blog post. Many thanks to <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> and <a href="http://www.ccs.neu.edu/home/matthias/">Matthias Felleisen</a> for their work on creating <code>syntax/parse</code>, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.</p><p>Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.</p><ol class="footnotes"></ol></article>Managing application configuration with Envyhttps://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/https://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/30 Aug 2015<article><p>Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, <a href="http://12factor.net">The Twelve-Factor App</a> provides a set of standards for keeping web apps sane, and <a href="http://12factor.net/config">one of those guidelines advises keeping configuration in the environment</a>.</p><p><a href="https://github.com/lexi-lambda/envy">Envy</a> is the declarative bridge between Racket code and the outside world of the environment.</p><h2><a name="introducing-envy"></a>Introducing Envy</h2><p>I built Envy to distill the common tasks needed when working with environment variables into a single, declarative interface that eliminates boilerplate and makes it easy to see which environment variables an application depends on (instead of having them littered throughout the codebase). Using it is simple. Just require <code>envy</code> and you're good to go.</p><p>The best way to use Envy is to create a "manifest" module that declares all the environment variables your application might use. For example, the following module is a manifest that describes an application that uses three environment variables:</p><pre><code class="pygments"><span class="c1">; environment.rkt</span> +<span class="kn">#lang </span><span class="nn">typed/racket/base</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">envy</span><span class="p">)</span> + +<span class="p">(</span><span class="n">define/provide-environment</span> +<span class="w"> </span><span class="n">api-token</span> +<span class="w"> </span><span class="p">[</span><span class="n">log-level</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Symbol</span><span class="w"> </span><span class="kd">#:default</span><span class="w"> </span><span class="o">&#39;</span><span class="ss">info</span><span class="p">]</span> +<span class="w"> </span><span class="p">[</span><span class="n">parallel?</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">Boolean</span><span class="p">])</span></code></pre><p>When this module is required, Envy will automatically do the following:</p><ol><li><p>Envy will check the values of three environment variables: <code>API_TOKEN</code>, <code>LOG_LEVEL</code>, and <code>PARALLEL</code>.</p></li><li><p>If either <code>API_TOKEN</code> or <code>PARALLEL</code> is not set, an error will be raised:</p><pre><code>envy: The required environment variable "API_TOKEN" is not defined. +</code></pre></li><li><p>The values for <code>LOG_LEVEL</code> and <code>PARALLEL</code> will be parsed to match their type annotations.</p></li><li><p>If <code>LOG_LEVEL</code> is not set, it will use the default value, <code>'info</code>.</p></li><li><p>The values will be stored in <code>api-token</code>, <code>log-level</code>, and <code>parallel?</code>, all of which will be provided by the enclosing module.</p></li></ol><p>Now just <code>(require (prefix-in env: "environment.rkt"))</code>, and the environment variables are guaranteed to be available in your application's code.</p><h2><a name="working-with-typed-racket"></a>Working with Typed Racket</h2><p>As you may have noticed by the example above, Envy is built with Typed Racket in mind. In fact, <code>define/provide-environment</code> will <em>only</em> work within a Typed Racket module, but that doesn't mean Envy can't be used with plain Racket—the manifest module can always be required by any kind of Racket module.</p><p>However, when using Typed Racket, Envy provides additional bonuses. Environment variables are inherently untyped—they're all just strings—but Envy assigns the proper type to each environment variable automatically, so no casting is necessary.</p><pre><code>&gt; parallel? +- : Boolean +#t +</code></pre><p>Envy really shines when using optional environment variables with the <code>#:default</code> option. The type of the value given to <code>#:default</code> doesn't need to be the same type of the environment variable itself, and if it isn't, Envy will assign the value a union type.</p><pre><code>&gt; (define-environment + [num-threads : Positive-Integer #:default #f]) +&gt; num-threads +- : (U Positive-Integer #f) +#f +</code></pre><p>This added level of type-safety means it's easy to manage optional variables that don't have reasonable defaults: the type system will enforce that all code considers the possibility that such variables do not exist.</p><h2><a name="and-more"></a>And more...</h2><p>To see the full set of features that Envy already provides, <a href="https://lexi-lambda.github.io/envy/envy.html">take a look at the documentation</a>. That said, this is just the first release based on my initial use-cases, but I'm sure there are more features Envy could have to accommodate common application configuration patterns. If you have an idea that could make Envy better, <a href="https://github.com/lexi-lambda/envy/issues">open an issue and make a suggestion</a>! I already have plans for a <code>#lang envy</code> DSL, which will hopefully cut the boilerplate out in its entirety.</p><p>And finally, to give credit where credit is due, Envy is heavily inspired by <a href="https://github.com/eval/envied">Envied</a> (both in name and function), an environment variable manager for Ruby, which I've used to great effect.</p><p>Try it out!</p><ul><li><p><code>raco pkg install envy</code></p></li><li><p><a href="https://github.com/lexi-lambda/envy">Envy on GitHub</a></p></li><li><p><a href="https://lexi-lambda.github.io/envy/envy.html">Envy documentation</a></p></li></ul><ol class="footnotes"></ol></article>Deploying Racket applications on Herokuhttps://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/https://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/22 Aug 2015<article><p><a href="https://www.heroku.com">Heroku</a> is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.</p><h2><a name="building-the-server"></a>Building the server</h2><p>Racket's <a href="http://docs.racket-lang.org/web-server/index.html">web-server</a> package makes building and running a simple server incredibly easy. Here's all the code we'll need to get going:</p><pre><code class="pygments"><span class="kn">#lang </span><span class="nn">racket</span> + +<span class="p">(</span><span class="k">require</span><span class="w"> </span><span class="n">web-server/servlet</span> +<span class="w"> </span><span class="n">web-server/servlet-env</span><span class="p">)</span> + +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="n">req</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">response/xexpr</span> +<span class="w"> </span><span class="o">&#39;</span><span class="p">(</span><span class="ss">html</span><span class="w"> </span><span class="p">(</span><span class="ss">head</span><span class="w"> </span><span class="p">(</span><span class="ss">title</span><span class="w"> </span><span class="s2">"Racket Heroku App"</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="ss">body</span><span class="w"> </span><span class="p">(</span><span class="ss">h1</span><span class="w"> </span><span class="s2">"It works!"</span><span class="p">)))))</span> + +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span></code></pre><p>Running the above file will start up the server on the default port, 8080. When running on Heroku, however, we're required to bind to the port that Heroku provides via the <code>PORT</code> environment variable. We can access this using the Racket <code>getenv</code>[racket] function.</p><p>Additionally, the Racket web server specifically binds to localhost, but Heroku doesn't allow that restriction, so we need to pass <code>#f</code> for the <code>#:listen-ip</code> argument.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="n">port</span><span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="nb">string-&gt;number</span><span class="w"> </span><span class="p">(</span><span class="nb">getenv</span><span class="w"> </span><span class="s2">"PORT"</span><span class="p">))</span> +<span class="w"> </span><span class="mi">8080</span><span class="p">))</span> +<span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span><span class="p">)</span></code></pre><p>Also, by default, <code>serve/servlet</code>[racket] will open a web browser automatically when the program is run, which is very useful for rapid prototyping within something like DrRacket, but we'll want to turn that off.</p><pre><code class="pygments"><span class="p">(</span><span class="n">serve/servlet</span><span class="w"> </span><span class="n">start</span> +<span class="w"> </span><span class="kd">#:servlet-path</span><span class="w"> </span><span class="s2">"/"</span> +<span class="w"> </span><span class="kd">#:listen-ip</span><span class="w"> </span><span class="no">#f</span> +<span class="w"> </span><span class="kd">#:port</span><span class="w"> </span><span class="n">port</span> +<span class="w"> </span><span class="kd">#:command-line?</span><span class="w"> </span><span class="no">#t</span><span class="p">)</span></code></pre><p>That's it! Now we have a Racket web server that can run on Heroku. Obviously it's not a very interesting application right now, but that's fine for our purposes.</p><h2><a name="setting-up-our-app-for-heroku"></a>Setting up our app for Heroku</h2><p>The next step is to actually create an app on Heroku. Don't worry—it's free! That said, explaining precisely how Heroku works is outside the scope of this article. Just make an account, then create an app. I called mine "racket-heroku-sample". Once you've created an app and set up Heroku's command-line tool, you can specify the proper buildpack:</p><pre><code class="pygments">$<span class="w"> </span>git<span class="w"> </span>init +$<span class="w"> </span>heroku<span class="w"> </span>git:remote<span class="w"> </span>-a<span class="w"> </span>racket-heroku-sample +$<span class="w"> </span>heroku<span class="w"> </span>buildpacks:set<span class="w"> </span>https://github.com/lexi-lambda/heroku-buildpack-racket</code></pre><p>We'll also need to pick a particular Racket version before we deploy our app. At the time of this writing, Racket 6.2.1 is the latest version, so I just set the <code>RACKET_VERSION</code> environment variable as follows:</p><pre><code class="pygments">$<span class="w"> </span>heroku<span class="w"> </span>config:set<span class="w"> </span><span class="nv">RACKET_VERSION</span><span class="o">=</span><span class="m">6</span>.2.1</code></pre><p>Now there's just one thing left to do before we can push to Heroku: we need to tell Heroku what command to use to run our application. To do this, we use something called a "Procfile" that contains information about the process types for our app. Heroku supports multiple processes of different types, but we're just going to have a single web process.</p><p>Specifically, we just want to run our <code>serve.rkt</code> module. The Racket buildpack installs the repository as a package, so we can run <code>racket</code> with the <code>-l</code> flag to specify a module path, which will be more robust than specifying a filesystem path directly. Therefore, our Procfile will look like this:</p><pre><code>web: racket -l sample-heroku-app/server +</code></pre><p>Now all that's left to do is push our repository to Heroku's git remote. Once the build completes, we can <a href="https://racket-heroku-sample.herokuapp.com">navigate to our app's URL and actually see it running live</a>!</p><h2><a name="conclusion"></a>Conclusion</h2><p>That's all that's needed to get a Racket app up and running on Heroku, but it probably isn't the best way to manage a real application. Usually it's best to use a continuous integration service to automatically deploy certain GitHub branches to Heroku, after running the tests, of course. Also, a real application would obviously be a little more complicated.</p><p>That said, this provides the foundation and shell. If you'd like to see the sample app used in this post, you can <a href="https://github.com/lexi-lambda/racket-sample-heroku-app">find it on GitHub here</a>. For more details on the buildpack itself, <a href="https://github.com/lexi-lambda/heroku-buildpack-racket">it's also available on GitHub here</a>.</p><ol class="footnotes"></ol></article>Automatically deploying a Frog-powered blog to GitHub pageshttps://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/https://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/18 Jul 2015<article><p>So, I have a blog now. It's a simple static blog, but what's unique about it is that it's powered by Racket; specifically, it uses <a href="http://www.greghendershott.com">Greg Hendershott</a>'s fantastic <a href="https://github.com/greghendershott/frog">Frog</a> tool. I've taken this and moulded it to my tastes to build my blog, including configuring automatic deployment via <a href="https://travis-ci.org">Travis CI</a>, so my blog is always up-to-date.</p><h2><a name="setting-up-frog"></a>Setting up Frog</h2><p>I should note that Frog itself was wonderfully easy to drop in and get running. Just following the readme, a simple <code>raco pkg install frog</code> followed by <code>raco frog --init</code> and <code>raco frog -bp</code> created a running blog and opened it in my web browser. There was nothing more to it. Once that's done, all it takes to write a blog post is <code>raco frog -n "Post Title"</code>, and you're good to go.</p><p>By default, Frog uses Bootstrap, which provides a lot of the necessary scaffolding for you, but I opted to roll my own layout using flexbox. I also decided to use <a href="http://sass-lang.com">Sass</a> for my stylesheets, potentially with support for <a href="http://coffeescript.org">CoffeeScript</a> later, so I wanted to have a good flow for compiling all the resources for deployment. To do that, I used <a href="http://gulpjs.com">Gulp</a> in conjunction with <a href="https://www.npmjs.com">NPM</a> for build and dependency management.</p><p>Going this route has a few advantages, primarily the fact that updating dependencies becomes much easier, and I can build and deploy my blog with just a couple of commands without needing to commit compiled or minified versions of my sources to version control.</p><h2><a name="configuring-automatic-deployment-with-travis"></a>Configuring automatic deployment with Travis</h2><p>Once Frog itself was configured and my styling was finished, I started looking into how to deploy my blog to a GitHub page without needing to check in any of the generated files to source control. I found a couple of resources, the most useful one being <a href="https://gist.github.com/domenic/ec8b0fc8ab45f39403dd">this Gist</a>, which describes how to set up deployment for any project. The basic idea is to create a deployment script which will automatically generate your project, initialize a git repository with the generated files, and push to GitHub's special <code>gh-pages</code> branch.</p><p>To make this easy, Frog can be configured to output to a separate directory via the <code>.frogrc</code> configuration file. I chose to output to the <code>out</code> directory:</p><pre><code>output-dir = out +</code></pre><p>I also configured my Gulp build to output my CSS into the same output directory. Now, all that's necessary in order to deploy the blog to GitHub is to initialize a Git repository in the output directory, and push the files to the remote branch.</p><pre><code>$ cd out +$ git init +$ git add . +$ git commit -m "Deploy to GitHub Pages" +$ git push --force "$REMOTE_URL" master:gh-pages +</code></pre><p>The next step is to configure Travis so that it can securely push to the GitHub repository with the required credentials. This can be done with Travis's <a href="http://docs.travis-ci.com/user/encryption-keys/">encryption keys</a> along with a GitHub <a href="https://github.com/settings/tokens">personal access token</a>. Just install the Travis CLI client, copy the access token, and run a command:</p><pre><code>$ gem install travis +$ travis encrypt GH_TOKEN=&lt;access token...&gt; +</code></pre><p>The output of that command is an encrypted value to be placed in an environment variable in the project's <code>.travis.yml</code> configuration file. The URL for the repository on GitHub will also need to be specified as well:</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span></code></pre><p>Now all that's left is configuring the <code>.travis.yml</code> to run Frog. Since Travis doesn't natively support Racket at the time of this writing, the choice of "language" is somewhat arbitrary, but since I want Pygments installed for syntax highlighting, I set my project type to <code>python</code>, then installed Racket and Frog as pre-installation steps.</p><pre><code class="pygments"><span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/&lt;gh-username&gt;/&lt;gh-repo&gt;.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;encrypted data...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span></code></pre><p>(It might be worth noting that Greg Hendershott <em>also</em> maintains the repository that contains the above Travis build script!)</p><p>Finally, in my case, I wasn't deploying to a project-specific GitHub page. Instead, I wanted to deploy to my user page, which uses <code>master</code>, not <code>gh-pages</code>. Obviously, I didn't want Travis running on my <code>master</code> branch, since it would be deploying to that, so I added a branch whitelist:</p><pre><code class="pygments"><span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span></code></pre><p>All that was left to do was to write up the actual deployment script to be used by Travis. Based on the one provided in the above Gist, mine looked like this:</p><pre><code class="pygments"><span class="ch">#!/bin/bash</span> +<span class="nb">set</span><span class="w"> </span>-ev<span class="w"> </span><span class="c1"># exit with nonzero exit code if anything fails</span> + +<span class="c1"># clear the output directory</span> +rm<span class="w"> </span>-rf<span class="w"> </span>out<span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nb">exit</span><span class="w"> </span><span class="m">0</span><span class="p">;</span> + +<span class="c1"># build the blog files + install pygments for highlighting support</span> +npm<span class="w"> </span>install +npm<span class="w"> </span>run<span class="w"> </span>build +pip<span class="w"> </span>install<span class="w"> </span>pygments +raco<span class="w"> </span>frog<span class="w"> </span>--build + +<span class="c1"># go to the out directory and create a *new* Git repo</span> +<span class="nb">cd</span><span class="w"> </span>out +git<span class="w"> </span>init + +<span class="c1"># inside this git repo we&#39;ll pretend to be a new user</span> +git<span class="w"> </span>config<span class="w"> </span>user.name<span class="w"> </span><span class="s2">"Travis CI"</span> +git<span class="w"> </span>config<span class="w"> </span>user.email<span class="w"> </span><span class="s2">"&lt;your@email.here&gt;"</span> + +<span class="c1"># The first and only commit to this new Git repo contains all the</span> +<span class="c1"># files present with the commit message "Deploy to GitHub Pages".</span> +git<span class="w"> </span>add<span class="w"> </span>. +git<span class="w"> </span>commit<span class="w"> </span>-m<span class="w"> </span><span class="s2">"Deploy to GitHub Pages"</span> + +<span class="c1"># Force push from the current repo&#39;s master branch to the remote</span> +<span class="c1"># repo. (All previous history on the branch will be lost, since we are</span> +<span class="c1"># overwriting it.) We redirect any output to /dev/null to hide any sensitive</span> +<span class="c1"># credential data that might otherwise be exposed.</span> +git<span class="w"> </span>push<span class="w"> </span>--force<span class="w"> </span>--quiet<span class="w"> </span><span class="s2">"https://</span><span class="si">${</span><span class="nv">GH_TOKEN</span><span class="si">}</span><span class="s2">@</span><span class="si">${</span><span class="nv">GH_REF</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>master<span class="w"> </span>&gt;<span class="w"> </span>/dev/null<span class="w"> </span><span class="m">2</span>&gt;<span class="p">&amp;</span><span class="m">1</span></code></pre><p>For reference, my final <code>.travis.yml</code> looked like this:</p><pre><code class="pygments"><span class="nt">language</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">python</span> +<span class="nt">python</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="s">&#39;3.4&#39;</span> + +<span class="nt">branches</span><span class="p">:</span> +<span class="w"> </span><span class="nt">only</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">source</span> + +<span class="nt">env</span><span class="p">:</span> +<span class="w"> </span><span class="nt">global</span><span class="p">:</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">GH_REF</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;github.com/lexi-lambda/lexi-lambda.github.io.git&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">secure</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">&lt;long secure token...&gt;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_DIR</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;~/racket&#39;</span> +<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">RACKET_VERSION</span><span class="p">:</span><span class="w"> </span><span class="s">&#39;6.2&#39;</span> + +<span class="nt">before_install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git clone https://github.com/greghendershott/travis-racket.git</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cat travis-racket/install-racket.sh | bash</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="${RACKET_DIR}/bin:${PATH}"</span> + +<span class="nt">install</span><span class="p">:</span> +<span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">raco pkg install --deps search-auto frog</span> + +<span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bash ./deploy.sh</span></code></pre><p>That's it! Now I have a working blog that I can publish just by pushing to the <code>source</code> branch on GitHub.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/rails.atom.xml b/feeds/rails.atom.xml new file mode 100644 index 0000000..c979644 --- /dev/null +++ b/feeds/rails.atom.xml @@ -0,0 +1,13 @@ +Posts tagged ‘rails’ | Alexis King’s Blog2015-09-23T00:00:00ZCanonical factories for testing with factory_girl_api2015-09-23T00:00:00Z2015-09-23T00:00:00ZAlexis King<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/rails.rss.xml b/feeds/rails.rss.xml new file mode 100644 index 0000000..2e6d25a --- /dev/null +++ b/feeds/rails.rss.xml @@ -0,0 +1,13 @@ +Posts tagged ‘rails’ | Alexis King’s BlogPosts tagged ‘rails’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/rails.html23 Sep 201523 Sep 201560Canonical factories for testing with factory_girl_apihttps://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/https://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/23 Sep 2015<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/ruby.atom.xml b/feeds/ruby.atom.xml new file mode 100644 index 0000000..5e211f9 --- /dev/null +++ b/feeds/ruby.atom.xml @@ -0,0 +1,13 @@ +Posts tagged ‘ruby’ | Alexis King’s Blog2015-09-23T00:00:00ZCanonical factories for testing with factory_girl_api2015-09-23T00:00:00Z2015-09-23T00:00:00ZAlexis King<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/ruby.rss.xml b/feeds/ruby.rss.xml new file mode 100644 index 0000000..cec3195 --- /dev/null +++ b/feeds/ruby.rss.xml @@ -0,0 +1,13 @@ +Posts tagged ‘ruby’ | Alexis King’s BlogPosts tagged ‘ruby’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/ruby.html23 Sep 201523 Sep 201560Canonical factories for testing with factory_girl_apihttps://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/https://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/23 Sep 2015<article><p>Modern web applications are often built as <em>single-page apps</em>, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.</p><p>To attempt to address a fraction of this problem, I built <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a>, a way to share context setup between both sides of the application.</p><h2><a name="a-brief-overview-of-factory-girl"></a>A brief overview of factory_girl</h2><p>In the land of Ruby and Rails, <a href="https://github.com/thoughtbot/factory_girl">factory_girl</a> is a convenient gem for managing factories for models. Out of the box, it integrates with Rails' default ORM, ActiveRecord, and provides declarative syntax for describing what attributes factories should initialize. For example, a factory declaration used to create a widget might look like this:</p><pre><code class="pygments"><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">define</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">factory</span><span class="w"> </span><span class="ss">:widget</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">sequence</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">|</span><span class="nb">id</span><span class="o">|</span><span class="w"> </span><span class="s1">&#39;Widget #&#39;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nb">id</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">10</span> + +<span class="w"> </span><span class="n">trait</span><span class="w"> </span><span class="ss">:expensive</span><span class="w"> </span><span class="k">do</span> +<span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="mi">1000</span> +<span class="w"> </span><span class="k">end</span> +<span class="w"> </span><span class="k">end</span> +<span class="k">end</span></code></pre><p>This makes it easy to create new instances of <code>Widget</code> and use them for unit tests. For example, this would create and persist a widget with a unique name and a price of 10 units:</p><pre><code class="pygments"><span class="n">widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span></code></pre><p>We can also create more expensive widgets by using the <code>:expensive</code> trait.</p><pre><code class="pygments"><span class="n">expensive_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span></code></pre><p>Any number of traits can be specified at once. Additionally, it is possible to override individual attributes manually.</p><pre><code class="pygments"><span class="n">fancy_widget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="no">FactoryGirl</span><span class="o">.</span><span class="n">create</span><span class="w"> </span><span class="ss">:widget</span><span class="p">,</span><span class="w"> </span><span class="ss">:expensive</span><span class="p">,</span><span class="w"> </span><span class="nb">name</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span></code></pre><p>It works well, and it keeps initialization boilerplate out of individual tests.</p><h2><a name="testing-on-the-front-end"></a>Testing on the front-end</h2><p>Trouble arises when we need to write tests for the JavaScript application that use the same models. Suddenly, we need to duplicate the same kind of logic in our front-end tests. We might start out by setting up object state manually:</p><pre><code class="pygments"><span class="kd">var</span><span class="w"> </span><span class="nx">fancyWidget</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Widget</span><span class="p">({</span> +<span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="p">,</span> +<span class="w"> </span><span class="nx">price</span><span class="o">:</span><span class="w"> </span><span class="mf">1000</span> +<span class="p">});</span></code></pre><p>Things can quickly get out of hand when models grow complex. Even if we use a factory library in JavaScript, it's possible for our front-end factories to diverge from their back-end counterparts. This means our integration tests will fail, but our unit tests will still blindly pass. Having to duplicate all that logic in two places is dangerous. It would be nice to have a <em>single, canonical source</em> for all of our factories.</p><h3><a name="reusing-server-side-factories-with-factory-girl-api"></a>Reusing server-side factories with factory_girl_api</h3><p>To help alleviate this problem, I created the <a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a> gem for Rails and the <a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a> Bower package for Angular. These packages cooperate with each other to allow server-side factories to be used in JavaScript tests.</p><p>The Angular module provides a service with syntax comparable to factory_girl itself. Both traits and custom attributes are supported:</p><pre><code class="pygments"><span class="nx">FactoryGirl</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="s1">&#39;widget&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;expensive&#39;</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">name</span><span class="o">:</span><span class="w"> </span><span class="s1">&#39;Fancy Widget&#39;</span><span class="w"> </span><span class="p">});</span></code></pre><p>In this case, however, a round-trip API call must be made to the server in order to call the factory and return the result. Because of this, the Angular version of FactoryGirl returns a promise that is resolved with the serialized version of the model, which can then be used as sample data in unit tests.</p><h3><a name="the-problems-with-relying-on-the-server-for-data"></a>The problems with relying on the server for data</h3><p>In my preliminary use of this tool, it works. In many ways, it's much nicer than duplicating logic in both places. However, I'm not <em>completely</em> convinced it's the right solution yet.</p><p>First of all, it couples the front-end to the back-end, even during unit testing, which is disappointing. It means that a server needs to be running (in test mode) in order for the tests to run at all. For the kinds of projects I work on, this isn't really a bad thing, and the benefits of the reduced duplication far outweigh the downsides.</p><p>My real concern is that this solves a very small facet of the general problem with fragile front-end test suites. Single-page applications usually depend wholly on their integration with back-end APIs. If those APIs change, the tests will continue to happily pass as long as the API is simply mocked, which seems to be the usual solution in the front-end universe. This is, frankly, unacceptable in real application development.</p><h3><a name="potential-improvements-and-other-paths-to-success"></a>Potential improvements and other paths to success</h3><p>I am ultimately unsatisfied with this approach, but writing brittle end-to-end integration tests is not the solution. This <em>kind</em> of thing may be a step in the right direction: writing tests that aren't really pure unit tests, but also aren't fragile full-stack integration tests. This is a middle-ground that seems infrequently traveled, perhaps due to a lack of tooling (or perhaps because it just doesn't work). I don't know.</p><p>Either way, I'm interested in where this is headed, and I'll be curious to see if I run into any roadblocks using the workflow I've created. If anyone else is interested in playing with these two libraries, the READMEs are much more comprehensive than what I've covered here. Take a look, and give them a spin!</p><ul><li><p><a href="https://github.com/lexi-lambda/factory_girl_api">factory_girl_api</a></p></li><li><p><a href="https://github.com/lexi-lambda/angular-factory-girl-api">angular-factory-girl-api</a></p></li></ul><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/testing.atom.xml b/feeds/testing.atom.xml new file mode 100644 index 0000000..b39ab9a --- /dev/null +++ b/feeds/testing.atom.xml @@ -0,0 +1,231 @@ +Posts tagged ‘testing’ | Alexis King’s Blog2017-06-29T00:00:00ZUnit testing effectful Haskell with monad-mock2017-06-29T00:00:00Z2017-06-29T00:00:00ZAlexis King<article><p>Nearly eight months ago, <a href="/blog/2016/10/03/using-types-to-unit-test-in-haskell/">I wrote a blog post about unit testing effectful Haskell code</a> using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, <a href="https://hackage.haskell.org/package/monad-mock">monad-mock</a>.</p><h2><a name="a-first-glance-at-monad-mock"></a>A first glance at monad-mock</h2><p>The monad-mock library is, first and foremost, designed to be <em>easy</em>. It doesn’t ask much from you, and it requires almost zero boilerplate.</p><p>The first step is to write an mtl-style interface that encodes an effect you want to mock. For example, you might want to test some code that interacts with the filesystem:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Now you just have to write your code as normal. For demonstration purposes, here’s a function that defines copying a file in terms of <code>readFile</code> and <code>writeFile</code>:</p><pre><code class="pygments"><span class="nf">copyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>Making this function work on the real filesystem is trivial, since we just need to define an instance of <code>MonadFileSystem</code> for <code>IO</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span></code></pre><p>But how do we test this? Well, we <em>could</em> run some real code in <code>IO</code>, which might not be so bad for such a simple function, but this seems like a bad idea. For one thing, a bad implementation of <code>copyFile</code> could do some pretty horrible things if it misbehaved and decided to overwrite important files, and if you’re constantly running a test suite whenever a file changes, it’s easy to imagine causing a lot of damage. Running tests against the real filesystem also makes tests slower and harder to parallelize, and it only gets much worse once you are doing more complex effects than interacting with the filesystem.</p><p>Using monad-mock, we can test this function in just a couple of lines of code:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Exception</span><span class="w"> </span><span class="p">(</span><span class="nf">evaluate</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock.TH</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Data.Function</span><span class="w"> </span><span class="p">((</span><span class="o">&amp;</span><span class="p">))</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Test.Hspec</span> + +<span class="nf">makeMock</span><span class="w"> </span><span class="s">"FileSystemAction"</span><span class="w"> </span><span class="p">[</span><span class="n">ts</span><span class="o">|</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="o">|</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"copyFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reads a file and writes its contents to another file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span></code></pre><p>That’s it!</p><p>The last two lines of the above snippet are the real interesting bits, which specify the actions that are expected to be executed, and it couples them with their results. You will find that if you tweak the list in any way, such as reordering the actions, eliminating one or both of them, or adding an additional action to the end, the test will fail. We could even turn this into a property-based test that generated arbitrary file paths and file contents.</p><p>Admittedly, in this trivial example, the mock is a little silly, since converting this into a property-based test would demonstrate how much we’ve basically just reimplemented the function in our test. However, once our function starts to do somewhat more complicated things, then our tests become more meaningful. Here’s a similar function that only copies a file if it is nonempty:</p><pre><code class="pygments"><span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This function has some logic which is very clearly <em>not</em> expressed in its type, and it would be difficult to encode that information into the type in a safe way. Fortunately, we can guarantee that it works by writing some tests:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">""</span><span class="w"> </span><span class="p">]</span></code></pre><p>These tests are much more useful, and they have some actual value to them. Imagine we had accidentally written <code>when</code> instead of <code>unless</code>, an easy typo to make. Our tests would fail with some useful error messages:</p><pre><code>1) copyNonemptyFile copies a file with contents + uncaught exception: runMockT: expected the following unexecuted actions to be run: + WriteFile "bar.txt" "contents" + +2) copyNonemptyFile does nothing with an empty file + uncaught exception: runMockT: expected end of program, called writeFile + given action: WriteFile "bar.txt" "" +</code></pre><p>You now know enough to write tests with monad-mock.</p><h2><a name="why-unit-test"></a>Why unit test?</h2><p>When the issue of testing is brought up in Haskell, it is often treated with a certain distaste by a portion of the community. There are some points I’ve seen a number of times, and though they take different forms, they boil down to two ideas:</p><ol><li><p>“Haskell code does not need tests because the type system can prove correctness.”</p></li><li><p>“Testing in Haskell is trivial because it is a pure language, and testing pure functions is easy.”</p></li></ol><p>I’ve been writing Haskell professionally for over a year now, and I can happily say that there <em>is</em> some truth to both of those things! When my Haskell code typechecks, I feel a confidence in it that I would not feel were I using a language with a less powerful type system. Furthermore, Haskell encourages a “pure core, impure shell” approach to system design that makes testing many things pleasant and straightforward, and it completely eliminates the worry of subtle nondeterminism leaking into tests.</p><p>That said, Haskell is not a proof assistant, and its type system cannot guarantee everything, especially for code that operates on the boundaries of what Haskell can control. For much the same reason, I find that my pure code is the code I am <em>least</em> likely to need to test, since it is also the code with the strongest type safety guarantees, operating on types in my application’s domain. In contrast, the effectful code is often what I find the most value in extensively testing, since it often contains the most subtle complexity, and it is frequently difficult or even impossible to encode into types.</p><p>Haskell has the power to provide remarkably strong correctness guarantees with a surprisingly small amount of effort by using a combination of tests and types, using each to accommodate for the other’s weaknesses and playing to each technique’s strengths. Some code is test-driven, other code is type-driven. Most code ends up being a mix of both. Testing is just a tool like any other, and it’s nice to feel confident in one’s ability to effectively structure code in a decoupled, testable manner.</p><h2><a name="why-mock"></a>Why mock?</h2><p>Even if you accept that testing is good, the question of whether or not to <em>mock</em> is a subtler issue. To some people, “unit testing” is synonymous with mocks. This is emphatically not true, and in fact, overly aggressive mocking is one of the best ways to make your test suite completely worthless. The monad-mock approach to mocking is a bit more principled than mocking in many dynamic, object-oriented languages, but it comes with many of the same drawbacks: mocks couple your tests to your implementation in ways that make them less valuable and less meaningful.</p><p>For the <code>MonadFileSystem</code> example above, I would actually probably <em>not</em> use a mock. Instead, I would use a <strong>fake</strong>, in-memory filesystem implementation:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)])</span> +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">second</span><span class="w"> </span><span class="n">sort</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">runStateT</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">fs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="o">&amp;</span> +<span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"readFile: no such file ‘"</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"’"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">filter</span><span class="w"> </span><span class="p">((</span><span class="o">/=</span><span class="w"> </span><span class="n">path</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fst</span><span class="p">)</span><span class="w"> </span><span class="n">fs</span></code></pre><p>The above snippet demonstrates how easy it is to define a <code>MonadFileSystem</code> implementation in terms of <code>StateT</code>, and while this may seem like a lot of boilerplate, it really isn’t. You have to write a fake <em>once</em> per interface, and the above block is a minuscule twelve lines of code. With this technique, you are still able to write tests that depend on the state of the filesystem before and after running the implementation, but you decouple yourself from the precise process of getting there:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"bar.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">),</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is better than using a mock, and I would highly recommend doing it if you can! However, a lot of real applications have to interact with services of much greater complexity than an idealized filesystem, and creating that sort of in-memory fake is not always practical. One such situation might be interacting with AWS CloudFormation, for example:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">createStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackName</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StackTemplate</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackId</span><span class="p">)</span> +<span class="w"> </span><span class="n">listStacks</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="p">[</span><span class="kt">StackSummaries</span><span class="p">])</span> +<span class="w"> </span><span class="n">describeStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackId</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackInfo</span><span class="p">)</span> +<span class="w"> </span><span class="c1">-- and so on...</span></code></pre><p>AWS is a very complex system, and it can do dozens of different things (and fail in dozens of different ways) based on an equally complex set of inputs. For example, in the above API, <code>createStack</code> needs to parse its template, which can be YAML or JSON, in order to determine which of many possible errors and behaviors can be produced, both on the initial call and on subsequent ones.</p><p>Creating a fake implementation of <em>AWS</em> is hardly feasible, and this is where a mock can be useful. By simply writing <code>makeMock "AWSAction" [ts| MonadAWS |]</code>, we can test functions that interact with AWS in a pure way without necessarily needing to replicate all of its complexity.</p><h3><a name="isolating-mocks"></a>Isolating mocks</h3><p>Of course, tests that use mocks provide less value than tests that use “smarter” fakes, since they are far more tightly coupled to the implementation, and it’s dramatically more likely that you will need to change the tests when you change the logic. To avoid this, it can be helpful to create multiple interfaces to the same thing: a high-level interface and a low-level one. If our above <code>MonadAWS</code> is a low-level interface, we could create a high-level counterpart that does precisely what our application needs:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDeploy</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">executeDeployment</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>When running our application “for real”, we would use <code>MonadAWS</code> to implement <code>MonadDeploy</code>:</p><pre><code class="pygments"><span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>The nice thing about this is we can actually test <code>executeDeploymentImpl</code> using a <code>MonadAWS</code> mock, so we can still have unit test coverage of the code on the boundaries of our system! Additionally, by containing the mock to a single place, we can test the rest of our code using a smarter fake implementation of <code>MonadDeploy</code>, helping to decouple our code from AWS’s complex API and improve the reliability and usefulness of our test suite.</p><p>They key point here is that mocking is just a small piece of the larger testing puzzle in <em>any</em> language, and that is just as true in Haskell. An overemphasis on mocking is an easy way to end up with a test suite that feels useless, probably because it is. Use mocks as a technique to insulate your application from the complexity in others’ APIs, then use more domain-specific testing techniques and type-level assertions to ensure the correctness of your logic.</p><h2><a name="how-monad-mock-works"></a>How monad-mock works</h2><p>If you’ve read this far and are convinced that monad-mock is useful, you may safely stop reading now. However, if you are interested in the details of what it actually does and what makes it tick, the rest of this blog post is going to focus on how the implementation works and how it compares to other techniques.</p><p>The centerpiece of monad-mock’s API is its monad transformer, <code>MockT</code>, which is a type constructor that accepts three types:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="p">)</span></code></pre><p>The <code>m</code> and <code>a</code> type variables obviously correspond to the usual monad transformer arguments, which represent the underlying monad and the result of the monadic computation, respectively. The <code>f</code> variable is more interesting, since it’s what makes <code>MockT</code> work at all, and it isn’t even a type: it’s a type constructor with kind <code>* -&gt; *</code>. What does it mean?</p><p>Looking at the type signature of <code>runMockT</code> gives us a little bit more information about what that <code>f</code> actually represents:</p><pre><code class="pygments"><span class="nf">runMockT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>This type signature provides two pieces of key information:</p><ol><li><p>The <code>f</code> parameter is constrained by the <code>Action f</code> constraint.</p></li><li><p>Running a mocked computation requires supplying a list of <code>WithResult f</code> values. This list corresponds to the list of expectations provided to <code>runMock</code> in earlier examples.</p></li></ol><p>To understand both of these things, it helps to examine the definition of an actual datatype that can have an <code>Action</code> instance. For the filesystem example, the action datatype looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Notice how each constructor clearly corresponds to one of the methods of <code>MonadFileSystem</code>, with a type to match. Now the purpose of the type provided to the <code>FileSystemAction</code> constructor (in this case <code>r</code>) should hopefully become clear: it represents the type of the value <em>produced</em> by each method. Also note that the type is completely phantom—it does not appear in negative position in any of the constructors.</p><p>With this in mind, we can take a look at the definition of <code>WithResult</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="p">(</span><span class="kt">:-&gt;</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span></code></pre><p>This is what defines the <code>(:-&gt;)</code> constructor from earlier in the blog post, and you can see that it effectively just represents a tuple of an action and a value of its associated result. It’s completely type-safe, since it ensures the result matches the type argument to the action.</p><p>Finally, this brings us to the <code>Action</code> class, which is not complex, but is unfortunately necessary:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">eqAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="n">showAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Notice that these methods are effectively just <code>(==)</code> and <code>show</code>, lifted to type constructors of kind <code>* -&gt; *</code>. One significant difference is that <code>eqAction</code> produces <code>Maybe (a :~: b)</code> instead of <code>Bool</code>, where <code>(:~:)</code> is from <code>Data.Type.Equality</code>. This is a type equality witness, which means a successful equality between two values allows the compiler to be sure that the two <em>types</em> are equal. This is necessary for the implementation of <code>runMockT</code> due to the phantom type in actions—in order to convince GHC that we can properly return the result of a mocked action, we need to assure it that the value we’re going to return is actually of the proper type.</p><p>Implementing this typeclass is not particularly burdensome, but it’s entirely boilerplate, so even if you want to define your own action type (that is, you don’t want to use <code>makeMock</code>), you can use the <code>deriveAction</code> function from <code>Control.Monad.Mock.TH</code> to derive an <code>Action</code> instance on an existing datatype.</p><h3><a name="connecting-the-mock-to-its-class"></a>Connecting the mock to its class</h3><p>Now that we have an action with which to mock a class, we need to actually define an instance of that class for <code>MockT</code>. For this process, monad-mock provides a <code>mockAction</code> function with the following type:</p><pre><code class="pygments"><span class="nf">mockAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span></code></pre><p>This function accepts two arguments: the name of the method being mocked and the action that represents the current call. This is easier to illustrate with an actual instance of <code>MonadFileSystem</code> using <code>MockT</code> and our <code>FileSystemAction</code> type:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MockT</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"readFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"writeFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>This allows <code>readFile</code> and <code>writeFile</code> to defer to the mock, and providing the names of the functions as strings helps monad-mock to produce useful error messages upon failure. Internally, <code>MockT</code> is a <code>StateT</code> that keeps track of a list of <code>WithResult f</code> values as its state. Each call to the mock checks the action against the internal list of calls, and if they match, it returns the associated result. Otherwise, it throws an exception.</p><p>This scheme is simple, but it seems to work remarkably well. There are some obvious enhancements that will probably be eventually necessary, like allowing action results that run in the underlying monad <code>m</code> in order to support things like <code>throwError</code> from <code>MonadError</code>, but so far, it hasn’t been necessary for what we’ve been using it for. Certain tricky signatures defy this simple technique, such as signatures where a monadic action appears in a negative position (that is, the signatures you need things like <a href="https://hackage.haskell.org/package/monad-control">monad-control</a> or <a href="https://hackage.haskell.org/package/monad-unlift">monad-unlift</a> for), but we’ve found that most of our effects don’t have any reason to include such signatures.</p><h2><a name="a-brief-comparison-with-free-r-monads"></a>A brief comparison with free(r) monads</h2><p>At this point, astute readers will likely be thinking about free monads, which parts of this technique greatly resemble. The representation of actions as GADTs is especially similar to <a href="https://hackage.haskell.org/package/freer">freer</a>, which does something extremely similar. Indeed, you can think of this technique as something that combines a freer-style representation with mtl-style classes. Given that freer already does this, you might ask yourself what the point is.</p><p>If you are already sold on free monads, monad-mock may very well be uninteresting to you. From the perspective of theoretical novelty, monad-mock is not anything new or different. However, there are a variety of practical reasons to prefer mtl over free, and it’s nice to see how easy it is to enjoy the testing benefits of free without too much extra effort.</p><p>An in-depth comparison between mtl and free is well outside the scope of this blog post. However, the key point is that this technique <em>only</em> affects test code, so the real runtime implementation will not be affected in any way. This means you can take advantage of the performance benefits and ecosystem support of mtl without sacrificing simple, expressive testing.</p><h2><a name="conclusion"></a>Conclusion</h2><p>To cap things off, I want to emphasize monad-mock’s role as a single part of a larger initiative we’ve been making for the better part of the past eighteen months. Haskell is a language with ever-evolving techniques and style, and it’s sometimes dizzying to figure out how to use all the pieces together to develop robust, maintainable applications. While monad-mock might not be anything drastically different from existing testing techniques, my hope is that it can provide an opinionated mechanism to make testing easy and accessible, even for complex interactions with other services and systems.</p><p>I’ve made an effort to make it abundantly clear in this blog post that monad-mock is <em>not</em> a silver bullet to testing, and in fact, I would prefer other techniques for ensuring correctness whenever possible. Even so, mocking is a nice tool to have in your toolbox, and it’s a good fallback to get even the worst APIs under test coverage.</p><p>If you want to try out monad-mock for yourself, <a href="https://hackage.haskell.org/package/monad-mock">take a look at the documentation on Hackage</a> and start playing around! It’s still early software, so it’s not the most proven or featureful, but we’ve managed to get mileage out of it already, all the same. If you find any problems, have a use case it does not support, or just find something about it unclear, please do not hesitate to <a href="https://github.com/cjdev/monad-mock">open an issue on the GitHub repository</a>—we obviously can’t fix issues we don’t know about.</p><p>Thanks as always to the many people who have contributed ideas that have shaped my philosophy and approach to testing and have helped provide the tools that make this library work. Happy testing!</p><ol class="footnotes"></ol></article>Using types to unit-test in Haskell2016-10-03T00:00:00Z2016-10-03T00:00:00ZAlexis King<article><p>Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.</p><p>Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.</p><h2><a name="first-an-aside-on-testing-philosophy"></a>First, an aside on testing philosophy</h2><p>Testing methodology is a controversial topic within the larger programming community, and there are a multitude of different approaches. This blog post is about <em>unit testing</em>, an already nebulous term with a number of different definitions. For the purposes of this post, I will define a unit test as a test that stubs out collaborators of the code under test in some way. Accomplishing that in Haskell is what this is primarily about.</p><p>I want to be clear that I do not think that unit tests are the only way to write tests, nor the best way, nor even always an applicable way. Depending on your domain, rigorous unit testing might not even make sense, and other forms of tests (end-to-end, integration, benchmarks, etc.) might fulfill your needs.</p><p>In practice, though, implementing those other kinds of tests seems to be well-documented in Haskell compared to pure, object-oriented style unit testing. As my Haskell applications have grown, I have found myself wanting a more fine-grained testing tool that allows me to both test a piece of my codebase in isolation and also use my domain-specific types. This blog post is about that.</p><p>With that disclaimer out of the way, let’s talk about testing in Haskell.</p><h2><a name="drawing-seams-using-types"></a>Drawing seams using types</h2><p>One of the primary attributes of unit tests in object-oriented languages, especially statically-typed ones, is the concept of “seams” within a codebase. These are internal boundaries between components of a system. Some boundaries are obvious—interactions with a database, manipulation of the file system, and performing I/O over the network, to name a few examples—but others are more subtle. Especially in larger codebases, it can be helpful to isolate two related but distinct pieces of functionality as much as possible, which makes them easier to reason about, even if they’re actually part of the same codebase.</p><p>In OO languages, these seams are often marked using interfaces, whether explicitly (in the case of static languages) or implicitly (in the case of dynamic ones). By programming to an interface, it’s possible to create “fake” implementations of that interface for use in unit tests, effectively making it possible to stub out code that isn’t directly relevant to the code being tested.</p><p>In Haskell, representing these seams is a lot less obvious. Consider a fairly trivial function that reverses a file’s contents on the file system:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span></code></pre><p>This function is impossible to test without testing against a real file system. It simply performs I/O directly, and there’s no way to “mock out” the file system for testing purposes. Now, admittedly, this function is so trivial that a unit test might not seem worth the cost, but consider a slightly more complicated function that interacts with a database:</p><pre><code class="pygments"><span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>It might now be a bit more clear that it could be useful to test the above function without running a real database and doing all the necessary context setup before each test case. Indeed, it would be nice if a test could just provide stubbed implementations for <code>fetchUser</code> and <code>fetchRecentPosts</code>, then make assertions about the output.</p><p>One way to solve this problem is to pass the results of those two functions to <code>renderUserProfile</code> as arguments, turning it into a pure function that could be easily tested. This becomes obnoxious for functions of even just slightly more complexity, though (it is not unreasonable to imagine needing a handful of different queries to render a user’s profile page), and it requires significantly restructuring code simply because the tests need it.</p><p>The above code is not only difficult to test, however—it has another problem, too. Specifically, both functions return <code>IO</code> values, which means they can effectively do <em>anything</em>. Haskell has a very strong type system for typing terms, but it doesn’t provide any guarantees about effects beyond a simple yes/no answer about function purity. Even though the <code>renderUserProfile</code> function should really only need to interact with the database, it could theoretically delete files, send emails, make HTTP requests, or do any number of other things.</p><p>Fortunately, it’s possible to solve <em>both</em> problems—a lack of testability and a lack of type safety—using the same general technique. This approach is reminiscent of the interface-based seams of object-oriented languages, but unlike most object-oriented approaches, it provides additional type safety guarantees without the need to explicitly modify the code to support some kind of dependency injection.</p><h3><a name="making-implicit-interfaces-explicit"></a>Making implicit interfaces explicit</h3><p>Statically typed, object-oriented languages provide interfaces as a language construct to encode certain kinds of contracts into the type system, and Haskell has something similar. Typeclasses are, in many ways, an analog to OO interfaces, and they can be used in a similar way. In the above case, let’s write down interfaces that the <code>reverseFile</code> and <code>renderUserProfile</code> functions can use:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span></code></pre><p>The really nice thing about these interfaces is that our function implementations don’t have to change <em>at all</em> to take advantage of them. In fact, all we have to change is their types:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span> + +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty neat, since we haven’t had to alter our code at all, but we’ve managed to completely decouple ourselves from <code>IO</code>. This has the direct effect of both making our code more abstract (we no longer rely on the “real” file system or a “real” database, which makes our code easier to test) and restricting what our functions can do (just from looking at the type signatures, we know what side-effects they can perform).</p><p>Of course, since we’re now coding against an interface, our code doesn’t actually do much of anything. If we want to actually use the functions we’ve written, we’ll have to define instances of <code>MonadFS</code> and <code>MonadDB</code>. When actually running our code, we’ll probably still use <code>IO</code> (or some monad transformer stack with <code>IO</code> at the bottom), so we can define trivial instances for that existing use case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchUser</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchRecentPosts</span></code></pre><p>Even if we go no further, <strong>this is already incredibly useful</strong>. By restricting the sorts of effects our functions can perform at the type level, it becomes a lot easier to see which code is interacting with what. This can be invaluable when working in a part of a moderately large codebase that you are unfamiliar with. Even if the only instance of these typeclasses is <code>IO</code>, the benefits are immediately apparent.</p><p>Of course, this blog post is about testing, so we’re going to go further and take advantage of these seams we’ve now drawn. The question is: how?</p><h2><a name="testing-with-typeclasses-an-initial-attempt"></a>Testing with typeclasses: an initial attempt</h2><p>Given that we now have functions depending on an interface instead of <code>IO</code>, we can create separate instances of our typeclasses for use in tests. Let’s start with the <code>renderUserProfile</code> function. We’ll create a simple wrapper around the <code>Identity</code> type, since we don’t actually care much about the “effects” of our <code>MonadDB</code> methods:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Functor.Identity</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">unTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now, we’ll create a trivial instance of <code>MonadDB</code> for <code>TestM</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa"</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">Post</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">postTitle</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span></code></pre><p>With this instance, it’s now possible to write a simple unit test of the <code>renderUserProfile</code> function that doesn’t need a real database running at all:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"renderUserProfile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows the user’s name"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="s">"Alyssa’s Profile"</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows a list of the user’s posts"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">li</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty nice, and running the above tests reveals a nice property of these kinds of isolated test cases: the test suite runs <em>really, really fast</em>. Communicating with a database, even in extremely simple ways, takes a measurable amount of time, especially with dozens of tests. In contrast, even with hundreds of tests, our unit test suite runs in less than a tenth of a second.</p><p>This all seems to be successful, so let’s try and apply the same testing technique to <code>reverseFile</code>.</p><h3><a name="testing-side-effectful-code"></a>Testing side-effectful code</h3><p>Looking at the type signature for <code>reverseFile</code>, we have a small problem:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Specifically, the return type is <code>()</code>. Making any assertions against the result of this function would be completely worthless, given that it’s guaranteed to be the same exact thing each time. Instead, <code>reverseFile</code> is inherently side-effectful, so we want to be able to test that it properly interacts with the file system in the correct way.</p><p>In order to do this, a simple wrapper around <code>Identity</code> won’t be enough, but we can replace it with something more powerful: <code>Writer</code>. Specifically, we can use a writer monad to “log” what gets called in order to test side-effects. We’ll start by creating a new <code>TestM</code> type, just like last time:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">])</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span></code></pre><p>Using this slightly more powerful type, we can write a useful instance of <code>MonadFS</code> that will track the argument given to <code>writeFile</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span></code></pre><p>Again, the instance is quite simple, but it now enables us to write a straightforward unit test for <code>reverseFile</code>:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span></code></pre><p>Again, quite simple to both implement and use, and the test itself is blindingly fast. There’s another problem, though, which is that we have technically left part of <code>reverseFile</code> untested: we’ve completely ignored the <code>path</code> argument.</p><p>In this contrived example, it may seem silly to test something so trivial, but in real code, it’s quite possible that one would care very much about testing multiple different aspects about a single function. When testing <code>renderUserProfile</code>, this was not hard, since we could reuse the same <code>TestM</code> type and <code>MonadDB</code> instance for both test cases, but in the <code>reverseFile</code> example, we’ve ignored the path entirely.</p><p>We <em>could</em> adjust our <code>MonadFS</code> instance to also track the path provided to each method, but this has a few problems. First, it means every test case would depend on all the various properties we are testing, which would mean updating every test case when we add a new one. It would also be simply impossible if we needed to track multiple types—in this particular case, it turns out that <code>String</code> and <code>FilePath</code> are actually the same type, but in practice, there may be a handful of disparate, incompatible types.</p><p>Both of the above issues could be fixed by creating a sum type and manually filtering out the relevant elements in each test case, but a much more intuitive approach would be to simply have a separate instance for each case. Unfortunately, in Haskell, creating a new instance means creating an entirely new type. To illustrate how much duplication that would entail, we could create the following type and instance for testing proper propagation of the <code>path</code> argument:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">])</span> + +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span></code></pre><p>Now it’s possible to add an extra test case that asserts that the proper path is provided to the two filesystem functions:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This works, but it’s ultimately unacceptably complicated. Our test harness code is now significantly larger than the actual tests themselves, and the amount of boilerplate is frustrating. Verbose test suites are especially bad, since forcing programmers to jump through hoops just to implement a single test reduces the likelihood that people will actually write good tests, if they write tests at all. In contrast, if writing tests is easy, then people will naturally write more of them.</p><p>The above strategy to writing tests is not good enough, but it does reveal a particular problem: in Haskell, typeclass instances are not first-class values that can be manipulated and abstracted over, they are static constructs that can only be managed by the compiler, and users do not have a direct way to modify them. With some cleverness, however, we can actually create an approximation of first-class typeclass dictionaries, which will allow us to dramatically simplify the above testing mechanism.</p><h2><a name="creating-first-class-typeclass-instances"></a>Creating first-class typeclass instances</h2><p>In order to provide an easy way to construct instances, we need a way to represent instances as ordinary Haskell values. This is not terribly difficult, given that instances are conceptually just records containing a collection of functions. For example, we could create a datatype that represents an instance of the <code>MonadFS</code> typeclass:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>To avoid namespace clashes with the actual method identifiers, the record fields are prefixed with an underscore, but otherwise, the translation is remarkably straightforward. Using this record type, we can easily create values that represent the two instances we defined above:</p><pre><code class="pygments"><span class="nf">contentInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> + +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>These two values represent two different implementations of <code>MonadFS</code>, but since they’re ordinary Haskell values, they can be manipulated and even <em>extended</em> like any other records. This can be extremely useful, since it makes it possible to create a sort of “base” instance, then have individual test cases override individual pieces of functionality piecemeal.</p><p>Of course, although we’ve written these two instances, we have no way to actually use them. After all, Haskell does not provide a way to explicitly provide typeclass dictionaries. Fortunately, we can create a sort of “proxy” type that will use a reader to thread the dictionary around explicitly, and the instance can defer to the dictionary’s implementation.</p><h3><a name="creating-an-instance-proxy"></a>Creating an instance proxy</h3><p>To represent our proxy type, we’ll use a combination of a <code>Writer</code> and a <code>ReaderT</code>; the former to implement the logging used by instances, and the latter to actually thread around the dictionary. Our type will look like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">log</span> +<span class="w"> </span><span class="p">)</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="n">inst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">inst</span><span class="p">)</span></code></pre><p>This might look rather complicated, and it is, but let’s break down exactly what it’s doing.</p><ol><li><p>The <code>TestM</code> type includes two type parameters. The first is the type of value that will be logged (hence the name <code>log</code>), which corresponds to the argument to <code>Writer</code> from previous incarnations of <code>TestM</code>. Unlike those types, though, we want this version to work with any <code>Monoid</code>, so we’ll make it a type parameter. The second parameter is simply the type of the current monadic value, as before.</p></li><li><p>The type itself is defined as a wrapper around a small monad transformer stack, the first of which is <code>ReaderT</code>. The state threaded around by the reader is, in this case, the instance dictionary, which is <code>MonadFSInst</code>.</p><p>However, recall that <code>MonadFSInst</code> accepts a type variable—the type of a monad itself—so we must provide <code>TestM log</code> as an argument to <code>MonadFSInst</code>. This slight bit of indirection allows us to tie the knot between the mutually dependent instances and proxy type.</p></li><li><p>The base monad in the transformer stack is <code>Writer</code>, which is used to actually implement the logging functionality, just like in prior cases. The only difference now is that the <code>log</code> type parameter now determines what the writer actually produces.</p></li><li><p>Finally, as before, we use <code>GeneralizedNewtypeDeriving</code> to derive all the relevant <code>mtl</code> classes, adding the somewhat wordy <code>MonadReader</code> constraint to the list.</p></li></ol><p>Using this single type, we can now implement a <code>MonadFS</code> instance that defers to the dictionary carried around within <code>TestM</code>’s reader state:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_readFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_writeFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This may seem somewhat boilerplate-y, and it is to some extent, but the important consideration is that this boilerplate only needs to be written <em>once</em>. With this in place, it’s now possible to write an arbitrary number of first-class instances that use the above mechanism without extending the mechanism at all.</p><p>To see what actually using this code would look like, let’s update the <code>reverseFile</code> tests to use the new <code>TestM</code> implementation, as well as the <code>contentInst</code> and <code>pathInst</code> dictionaries from earlier:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>We can do a little bit better, though. Really, the definitions of <code>contentInst</code> and <code>pathInst</code> are specific to each test case. With ordinary typeclass instances, we cannot scope them to any particular block, but since <code>MonadFSInst</code> is just an ordinary Haskell datatype, we can manipulate them just like any other Haskell values. Therefore, we can just inline those instances’ definitions into the test cases themselves to keep them closer to the actual tests.</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This is pretty good. We’re now able to create inline instances of our <code>MonadFS</code> typeclass, which allows us to write extremely concise unit tests using ordinary Haskell typeclasses as system seams. We’ve managed to cut down on the boilerplate considerably, though we still have a couple problems. For one, this example only uses a single typeclass containing only two methods. A real <code>MonadFS</code> typeclass would likely have at least a dozen methods for performing various filesystem operations, and writing out the instance dictionaries for every single method, even the ones that aren’t used within the code under test, would be pretty frustratingly verbose.</p><p>This problem is solvable, though. Since instances are just ordinary Haskell records, we can create a “base” instance that just throws an exception whenever the method is called:</p><pre><code class="pygments"><span class="nf">baseInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">baseInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_readFile’"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_writeFile’"</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Then code that only uses <code>readFile</code> could only override that particular method, for example:</p><pre><code class="pygments"><span class="kr">let</span><span class="w"> </span><span class="n">myInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">baseInst</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span></code></pre><p>Normally, of course, this would be a terrible idea. However, since this is all just test code, it can be extremely useful in quickly figuring out what methods need to be stubbed out for a particular test case. Since all the code actually gets run at test time, attempts to use unimplemented instance methods will immediately raise an error, informing the programmer which methods need to be implemented to make the test pass. This can also help to significantly cut down on the amount of effort it takes to implement each test.</p><p>Another problem is that our approach is specialized exclusively to <code>MonadFS</code>. What about functions that use both <code>MonadFS</code> <em>and</em> <code>MonadDB</code>, for example? Fortunately, that is not hard to solve, either. We can adapt the <code>MonadFSInst</code> type to include fields for all of the typeclasses relevant to our system, turning it into a generic test fixture of sorts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FixtureInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FixtureInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">-- MonadFS</span> +<span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="c1">-- MonadDB</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Updating <code>TestM</code> to use <code>FixtureInst</code> instead of <code>MonadFSInst</code> is trivial, and all the rest of the infrastructure still works. However, this means that every time a new typeclass is added, three things need to be updated:</p><ol><li><p>Its methods need to be added to the <code>FixtureInst</code> record.</p></li><li><p>Those methods need to be given error-raising defaults in the <code>baseInst</code> value.</p></li><li><p>An actual instance of the typeclass needs to be written for <code>TestM</code> that defers to the <code>FixtureInst</code> value.</p></li></ol><p>Furthermore, most of this manual manipulation of methods is required every time a particular typeclass changes, whether that means adding a method, removing a method, renaming a method, or changing a method’s type. This is especially frustrating given that all this code is really just mechanical boilerplate that could all be derived by the set of typeclasses being tested.</p><p>That last point is especially important: aside from the instances themselves, every piece of boilerplate above is obviously possible to generate from existing types alone. With that piece of information in mind, we can do even better: we can use Template Haskell.</p><h2><a name="removing-the-boilerplate-using-test-fixture"></a>Removing the boilerplate using <code>test-fixture</code></h2><p>The above code was not only rather boilerplate-heavy, it was pretty complicated. Fortunately, you don’t actually have to write it. Enter the library <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code></a>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture.TH</span> + +<span class="nf">mkFixture</span><span class="w"> </span><span class="s">"FixtureInst"</span><span class="w"> </span><span class="p">[</span><span class="kt">&#39;&#39;MonadFS</span><span class="p">,</span><span class="w"> </span><span class="kt">&#39;&#39;MonadDB</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">contentInst</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">pathInst</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p><strong>That’s it.</strong> The above code automatically generates everything you need to write fast, simple, deterministic unit tests in Haskell. The <code>mkFixture</code> function is a Template Haskell macro that expands into a definition quite similar to the <code>FixtureInst</code> type we wrote by hand, but since it’s automatically generated from the typeclass definitions, it never needs to be updated.</p><p>The <code>logTestFixture</code> function replaces the <code>logTestM</code> function we wrote by hand, but it works exactly the same. The <code>Control.Monad.TestFixture</code> library also exports a <code>log</code> function that is a synonym for <code>tell . singleton</code>, but using <code>tell</code> directly still works if you prefer.</p><p>The <code>mkFixture</code> function also generates a <code>Default</code> instance, which replaces the <code>baseInst</code> value defined earlier. It functions the same way, though, producing useful error messages that refer to the names of unimplemented typeclass methods that have not been stubbed out.</p><p>This blog post is not a <code>test-fixture</code> tutorial—indeed, it is much more complicated than a <code>test-fixture</code> tutorial would be, since it covers what the library is really doing under the hood—but if you’re interested, I would highly recommend you take a look at the <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code> documentation on Hackage</a>.</p><h2><a name="conclusion-credits-and-similar-techniques"></a>Conclusion, credits, and similar techniques</h2><p>This blog post came about as the result of a need my coworkers and I found when writing Haskell code; we wanted a way to write unit tests quickly and easily, but we didn’t find much advice from the rest of the Haskell ecosystem. The <code>test-fixture</code> library is the result of that exploratory work, and we currently use it to test a significant portion of our Haskell code.</p><p>It would be extremely unfair to suggest that I was the inventor of this technique or the inventor of the library. Two of my coworkers, <a href="https://github.com/jxv">Joe Vargas</a> and <a href="https://github.com/aztecrex">Greg Wiley</a>, came up with the general approach and wrote <code>Control.Monad.TestFixture</code>, and I simply wrote the Template Haskell macro to eliminate the boilerplate. With that in mind, I think I can say with some fairness that I think this technique is a joy to use when unit testing is a desirable goal, and I would definitely recommend it if you are interested in doing isolated testing in Haskell.</p><p>The general technique of using typeclasses to emulate effects was in part inspired by the well-known <code>mtl</code> library. An alternate approach to writing unit-testable Haskell code is using free monads, but overall, I prefer this approach over free monads because the typeclass constraints add type safety in ways that free monads do not (at least not without additional boilerplate), and this approach also lends itself well to static analysis-based boilerplate reduction techniques. It has its own tradeoffs, though, so if you’ve had success with free monads, then I certainly make no claim this is a superior approach, just one that I’ve personally found pleasant.</p><p>As a final note, if you <em>do</em> check out <code>test-fixture</code>, feel free to leave feedback by opening issues on <a href="https://github.com/cjdev/test-fixture/issues">the GitHub issue tracker</a>—even things like confusing documentation are worth a bug report.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/testing.rss.xml b/feeds/testing.rss.xml new file mode 100644 index 0000000..efce236 --- /dev/null +++ b/feeds/testing.rss.xml @@ -0,0 +1,231 @@ +Posts tagged ‘testing’ | Alexis King’s BlogPosts tagged ‘testing’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/testing.html29 Jun 201729 Jun 201760Unit testing effectful Haskell with monad-mockhttps://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/https://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/29 Jun 2017<article><p>Nearly eight months ago, <a href="/blog/2016/10/03/using-types-to-unit-test-in-haskell/">I wrote a blog post about unit testing effectful Haskell code</a> using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, <a href="https://hackage.haskell.org/package/monad-mock">monad-mock</a>.</p><h2><a name="a-first-glance-at-monad-mock"></a>A first glance at monad-mock</h2><p>The monad-mock library is, first and foremost, designed to be <em>easy</em>. It doesn’t ask much from you, and it requires almost zero boilerplate.</p><p>The first step is to write an mtl-style interface that encodes an effect you want to mock. For example, you might want to test some code that interacts with the filesystem:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Now you just have to write your code as normal. For demonstration purposes, here’s a function that defines copying a file in terms of <code>readFile</code> and <code>writeFile</code>:</p><pre><code class="pygments"><span class="nf">copyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>Making this function work on the real filesystem is trivial, since we just need to define an instance of <code>MonadFileSystem</code> for <code>IO</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span></code></pre><p>But how do we test this? Well, we <em>could</em> run some real code in <code>IO</code>, which might not be so bad for such a simple function, but this seems like a bad idea. For one thing, a bad implementation of <code>copyFile</code> could do some pretty horrible things if it misbehaved and decided to overwrite important files, and if you’re constantly running a test suite whenever a file changes, it’s easy to imagine causing a lot of damage. Running tests against the real filesystem also makes tests slower and harder to parallelize, and it only gets much worse once you are doing more complex effects than interacting with the filesystem.</p><p>Using monad-mock, we can test this function in just a couple of lines of code:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Exception</span><span class="w"> </span><span class="p">(</span><span class="nf">evaluate</span><span class="p">)</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.Mock.TH</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Data.Function</span><span class="w"> </span><span class="p">((</span><span class="o">&amp;</span><span class="p">))</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Test.Hspec</span> + +<span class="nf">makeMock</span><span class="w"> </span><span class="s">"FileSystemAction"</span><span class="w"> </span><span class="p">[</span><span class="n">ts</span><span class="o">|</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="o">|</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"copyFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reads a file and writes its contents to another file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span></code></pre><p>That’s it!</p><p>The last two lines of the above snippet are the real interesting bits, which specify the actions that are expected to be executed, and it couples them with their results. You will find that if you tweak the list in any way, such as reordering the actions, eliminating one or both of them, or adding an additional action to the end, the test will fail. We could even turn this into a property-based test that generated arbitrary file paths and file contents.</p><p>Admittedly, in this trivial example, the mock is a little silly, since converting this into a property-based test would demonstrate how much we’ve basically just reimplemented the function in our test. However, once our function starts to do somewhat more complicated things, then our tests become more meaningful. Here’s a similar function that only copies a file if it is nonempty:</p><pre><code class="pygments"><span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">copyNonemptyFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">unless</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This function has some logic which is very clearly <em>not</em> expressed in its type, and it would be difficult to encode that information into the type in a safe way. Fortunately, we can guarantee that it works by writing some tests:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">"contents"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="s">"bar.txt"</span><span class="w"> </span><span class="s">"contents"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">runMock</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="kt">:-&gt;</span><span class="w"> </span><span class="s">""</span><span class="w"> </span><span class="p">]</span></code></pre><p>These tests are much more useful, and they have some actual value to them. Imagine we had accidentally written <code>when</code> instead of <code>unless</code>, an easy typo to make. Our tests would fail with some useful error messages:</p><pre><code>1) copyNonemptyFile copies a file with contents + uncaught exception: runMockT: expected the following unexecuted actions to be run: + WriteFile "bar.txt" "contents" + +2) copyNonemptyFile does nothing with an empty file + uncaught exception: runMockT: expected end of program, called writeFile + given action: WriteFile "bar.txt" "" +</code></pre><p>You now know enough to write tests with monad-mock.</p><h2><a name="why-unit-test"></a>Why unit test?</h2><p>When the issue of testing is brought up in Haskell, it is often treated with a certain distaste by a portion of the community. There are some points I’ve seen a number of times, and though they take different forms, they boil down to two ideas:</p><ol><li><p>“Haskell code does not need tests because the type system can prove correctness.”</p></li><li><p>“Testing in Haskell is trivial because it is a pure language, and testing pure functions is easy.”</p></li></ol><p>I’ve been writing Haskell professionally for over a year now, and I can happily say that there <em>is</em> some truth to both of those things! When my Haskell code typechecks, I feel a confidence in it that I would not feel were I using a language with a less powerful type system. Furthermore, Haskell encourages a “pure core, impure shell” approach to system design that makes testing many things pleasant and straightforward, and it completely eliminates the worry of subtle nondeterminism leaking into tests.</p><p>That said, Haskell is not a proof assistant, and its type system cannot guarantee everything, especially for code that operates on the boundaries of what Haskell can control. For much the same reason, I find that my pure code is the code I am <em>least</em> likely to need to test, since it is also the code with the strongest type safety guarantees, operating on types in my application’s domain. In contrast, the effectful code is often what I find the most value in extensively testing, since it often contains the most subtle complexity, and it is frequently difficult or even impossible to encode into types.</p><p>Haskell has the power to provide remarkably strong correctness guarantees with a surprisingly small amount of effort by using a combination of tests and types, using each to accommodate for the other’s weaknesses and playing to each technique’s strengths. Some code is test-driven, other code is type-driven. Most code ends up being a mix of both. Testing is just a tool like any other, and it’s nice to feel confident in one’s ability to effectively structure code in a decoupled, testable manner.</p><h2><a name="why-mock"></a>Why mock?</h2><p>Even if you accept that testing is good, the question of whether or not to <em>mock</em> is a subtler issue. To some people, “unit testing” is synonymous with mocks. This is emphatically not true, and in fact, overly aggressive mocking is one of the best ways to make your test suite completely worthless. The monad-mock approach to mocking is a bit more principled than mocking in many dynamic, object-oriented languages, but it comes with many of the same drawbacks: mocks couple your tests to your implementation in ways that make them less valuable and less meaningful.</p><p>For the <code>MonadFileSystem</code> example above, I would actually probably <em>not</em> use a mock. Instead, I would use a <strong>fake</strong>, in-memory filesystem implementation:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="p">(</span><span class="kt">StateT</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)]</span> +<span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="p">[(</span><span class="kt">FilePath</span><span class="p">,</span><span class="w"> </span><span class="kt">String</span><span class="p">)])</span> +<span class="nf">fakeFileSystemT</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">second</span><span class="w"> </span><span class="n">sort</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="n">runStateT</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">fs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">get</span><span class="w"> </span><span class="o">&gt;&gt;=</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">lookup</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="o">&amp;</span> +<span class="w"> </span><span class="n">maybe</span><span class="w"> </span><span class="p">(</span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"readFile: no such file ‘"</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"’"</span><span class="p">)</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FakeFileSystemT</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">modify</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="nf">\</span><span class="n">fs</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="p">(</span><span class="n">path</span><span class="p">,</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span><span class="w"> </span><span class="kt">:</span><span class="w"> </span><span class="n">filter</span><span class="w"> </span><span class="p">((</span><span class="o">/=</span><span class="w"> </span><span class="n">path</span><span class="p">)</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">fst</span><span class="p">)</span><span class="w"> </span><span class="n">fs</span></code></pre><p>The above snippet demonstrates how easy it is to define a <code>MonadFileSystem</code> implementation in terms of <code>StateT</code>, and while this may seem like a lot of boilerplate, it really isn’t. You have to write a fake <em>once</em> per interface, and the above block is a minuscule twelve lines of code. With this technique, you are still able to write tests that depend on the state of the filesystem before and after running the implementation, but you decouple yourself from the precise process of getting there:</p><pre><code class="pygments"><span class="nf">describe</span><span class="w"> </span><span class="s">"copyNonemptyFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"copies a file with contents"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"bar.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">),</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"contents"</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"does nothing with an empty file"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="n">fs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">runIdentity</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">copyNonemptyFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="w"> </span><span class="s">"bar.txt"</span> +<span class="w"> </span><span class="o">&amp;</span><span class="w"> </span><span class="n">fakeFileSystemT</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="n">fs</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">(</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">)</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is better than using a mock, and I would highly recommend doing it if you can! However, a lot of real applications have to interact with services of much greater complexity than an idealized filesystem, and creating that sort of in-memory fake is not always practical. One such situation might be interacting with AWS CloudFormation, for example:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">createStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackName</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">StackTemplate</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackId</span><span class="p">)</span> +<span class="w"> </span><span class="n">listStacks</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="p">[</span><span class="kt">StackSummaries</span><span class="p">])</span> +<span class="w"> </span><span class="n">describeStack</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">StackId</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">AWSError</span><span class="w"> </span><span class="kt">StackInfo</span><span class="p">)</span> +<span class="w"> </span><span class="c1">-- and so on...</span></code></pre><p>AWS is a very complex system, and it can do dozens of different things (and fail in dozens of different ways) based on an equally complex set of inputs. For example, in the above API, <code>createStack</code> needs to parse its template, which can be YAML or JSON, in order to determine which of many possible errors and behaviors can be produced, both on the initial call and on subsequent ones.</p><p>Creating a fake implementation of <em>AWS</em> is hardly feasible, and this is where a mock can be useful. By simply writing <code>makeMock "AWSAction" [ts| MonadAWS |]</code>, we can test functions that interact with AWS in a pure way without necessarily needing to replicate all of its complexity.</p><h3><a name="isolating-mocks"></a>Isolating mocks</h3><p>Of course, tests that use mocks provide less value than tests that use “smarter” fakes, since they are far more tightly coupled to the implementation, and it’s dramatically more likely that you will need to change the tests when you change the logic. To avoid this, it can be helpful to create multiple interfaces to the same thing: a high-level interface and a low-level one. If our above <code>MonadAWS</code> is a low-level interface, we could create a high-level counterpart that does precisely what our application needs:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDeploy</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">executeDeployment</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span></code></pre><p>When running our application “for real”, we would use <code>MonadAWS</code> to implement <code>MonadDeploy</code>:</p><pre><code class="pygments"><span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadAWS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Deployment</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">DeployError</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span> +<span class="nf">executeDeploymentImpl</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span></code></pre><p>The nice thing about this is we can actually test <code>executeDeploymentImpl</code> using a <code>MonadAWS</code> mock, so we can still have unit test coverage of the code on the boundaries of our system! Additionally, by containing the mock to a single place, we can test the rest of our code using a smarter fake implementation of <code>MonadDeploy</code>, helping to decouple our code from AWS’s complex API and improve the reliability and usefulness of our test suite.</p><p>They key point here is that mocking is just a small piece of the larger testing puzzle in <em>any</em> language, and that is just as true in Haskell. An overemphasis on mocking is an easy way to end up with a test suite that feels useless, probably because it is. Use mocks as a technique to insulate your application from the complexity in others’ APIs, then use more domain-specific testing techniques and type-level assertions to ensure the correctness of your logic.</p><h2><a name="how-monad-mock-works"></a>How monad-mock works</h2><p>If you’ve read this far and are convinced that monad-mock is useful, you may safely stop reading now. However, if you are interested in the details of what it actually does and what makes it tick, the rest of this blog post is going to focus on how the implementation works and how it compares to other techniques.</p><p>The centerpiece of monad-mock’s API is its monad transformer, <code>MockT</code>, which is a type constructor that accepts three types:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="p">)</span></code></pre><p>The <code>m</code> and <code>a</code> type variables obviously correspond to the usual monad transformer arguments, which represent the underlying monad and the result of the monadic computation, respectively. The <code>f</code> variable is more interesting, since it’s what makes <code>MockT</code> work at all, and it isn’t even a type: it’s a type constructor with kind <code>* -&gt; *</code>. What does it mean?</p><p>Looking at the type signature of <code>runMockT</code> gives us a little bit more information about what that <code>f</code> actually represents:</p><pre><code class="pygments"><span class="nf">runMockT</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">a</span></code></pre><p>This type signature provides two pieces of key information:</p><ol><li><p>The <code>f</code> parameter is constrained by the <code>Action f</code> constraint.</p></li><li><p>Running a mocked computation requires supplying a list of <code>WithResult f</code> values. This list corresponds to the list of expectations provided to <code>runMock</code> in earlier examples.</p></li></ol><p>To understand both of these things, it helps to examine the definition of an actual datatype that can have an <code>Action</code> instance. For the filesystem example, the action datatype looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ReadFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="kt">WriteFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Notice how each constructor clearly corresponds to one of the methods of <code>MonadFileSystem</code>, with a type to match. Now the purpose of the type provided to the <code>FileSystemAction</code> constructor (in this case <code>r</code>) should hopefully become clear: it represents the type of the value <em>produced</em> by each method. Also note that the type is completely phantom—it does not appear in negative position in any of the constructors.</p><p>With this in mind, we can take a look at the definition of <code>WithResult</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="p">(</span><span class="kt">:-&gt;</span><span class="p">)</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">WithResult</span><span class="w"> </span><span class="n">f</span></code></pre><p>This is what defines the <code>(:-&gt;)</code> constructor from earlier in the blog post, and you can see that it effectively just represents a tuple of an action and a value of its associated result. It’s completely type-safe, since it ensures the result matches the type argument to the action.</p><p>Finally, this brings us to the <code>Action</code> class, which is not complex, but is unfortunately necessary:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">eqAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="p">)</span> +<span class="w"> </span><span class="n">showAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Notice that these methods are effectively just <code>(==)</code> and <code>show</code>, lifted to type constructors of kind <code>* -&gt; *</code>. One significant difference is that <code>eqAction</code> produces <code>Maybe (a :~: b)</code> instead of <code>Bool</code>, where <code>(:~:)</code> is from <code>Data.Type.Equality</code>. This is a type equality witness, which means a successful equality between two values allows the compiler to be sure that the two <em>types</em> are equal. This is necessary for the implementation of <code>runMockT</code> due to the phantom type in actions—in order to convince GHC that we can properly return the result of a mocked action, we need to assure it that the value we’re going to return is actually of the proper type.</p><p>Implementing this typeclass is not particularly burdensome, but it’s entirely boilerplate, so even if you want to define your own action type (that is, you don’t want to use <code>makeMock</code>), you can use the <code>deriveAction</code> function from <code>Control.Monad.Mock.TH</code> to derive an <code>Action</code> instance on an existing datatype.</p><h3><a name="connecting-the-mock-to-its-class"></a>Connecting the mock to its class</h3><p>Now that we have an action with which to mock a class, we need to actually define an instance of that class for <code>MockT</code>. For this process, monad-mock provides a <code>mockAction</code> function with the following type:</p><pre><code class="pygments"><span class="nf">mockAction</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Action</span><span class="w"> </span><span class="n">f</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MockT</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">r</span></code></pre><p>This function accepts two arguments: the name of the method being mocked and the action that represents the current call. This is easier to illustrate with an actual instance of <code>MonadFileSystem</code> using <code>MockT</code> and our <code>FileSystemAction</code> type:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFileSystem</span><span class="w"> </span><span class="p">(</span><span class="kt">MockT</span><span class="w"> </span><span class="kt">FileSystemAction</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"readFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">ReadFile</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">mockAction</span><span class="w"> </span><span class="s">"writeFile"</span><span class="w"> </span><span class="p">(</span><span class="kt">WriteFile</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>This allows <code>readFile</code> and <code>writeFile</code> to defer to the mock, and providing the names of the functions as strings helps monad-mock to produce useful error messages upon failure. Internally, <code>MockT</code> is a <code>StateT</code> that keeps track of a list of <code>WithResult f</code> values as its state. Each call to the mock checks the action against the internal list of calls, and if they match, it returns the associated result. Otherwise, it throws an exception.</p><p>This scheme is simple, but it seems to work remarkably well. There are some obvious enhancements that will probably be eventually necessary, like allowing action results that run in the underlying monad <code>m</code> in order to support things like <code>throwError</code> from <code>MonadError</code>, but so far, it hasn’t been necessary for what we’ve been using it for. Certain tricky signatures defy this simple technique, such as signatures where a monadic action appears in a negative position (that is, the signatures you need things like <a href="https://hackage.haskell.org/package/monad-control">monad-control</a> or <a href="https://hackage.haskell.org/package/monad-unlift">monad-unlift</a> for), but we’ve found that most of our effects don’t have any reason to include such signatures.</p><h2><a name="a-brief-comparison-with-free-r-monads"></a>A brief comparison with free(r) monads</h2><p>At this point, astute readers will likely be thinking about free monads, which parts of this technique greatly resemble. The representation of actions as GADTs is especially similar to <a href="https://hackage.haskell.org/package/freer">freer</a>, which does something extremely similar. Indeed, you can think of this technique as something that combines a freer-style representation with mtl-style classes. Given that freer already does this, you might ask yourself what the point is.</p><p>If you are already sold on free monads, monad-mock may very well be uninteresting to you. From the perspective of theoretical novelty, monad-mock is not anything new or different. However, there are a variety of practical reasons to prefer mtl over free, and it’s nice to see how easy it is to enjoy the testing benefits of free without too much extra effort.</p><p>An in-depth comparison between mtl and free is well outside the scope of this blog post. However, the key point is that this technique <em>only</em> affects test code, so the real runtime implementation will not be affected in any way. This means you can take advantage of the performance benefits and ecosystem support of mtl without sacrificing simple, expressive testing.</p><h2><a name="conclusion"></a>Conclusion</h2><p>To cap things off, I want to emphasize monad-mock’s role as a single part of a larger initiative we’ve been making for the better part of the past eighteen months. Haskell is a language with ever-evolving techniques and style, and it’s sometimes dizzying to figure out how to use all the pieces together to develop robust, maintainable applications. While monad-mock might not be anything drastically different from existing testing techniques, my hope is that it can provide an opinionated mechanism to make testing easy and accessible, even for complex interactions with other services and systems.</p><p>I’ve made an effort to make it abundantly clear in this blog post that monad-mock is <em>not</em> a silver bullet to testing, and in fact, I would prefer other techniques for ensuring correctness whenever possible. Even so, mocking is a nice tool to have in your toolbox, and it’s a good fallback to get even the worst APIs under test coverage.</p><p>If you want to try out monad-mock for yourself, <a href="https://hackage.haskell.org/package/monad-mock">take a look at the documentation on Hackage</a> and start playing around! It’s still early software, so it’s not the most proven or featureful, but we’ve managed to get mileage out of it already, all the same. If you find any problems, have a use case it does not support, or just find something about it unclear, please do not hesitate to <a href="https://github.com/cjdev/monad-mock">open an issue on the GitHub repository</a>—we obviously can’t fix issues we don’t know about.</p><p>Thanks as always to the many people who have contributed ideas that have shaped my philosophy and approach to testing and have helped provide the tools that make this library work. Happy testing!</p><ol class="footnotes"></ol></article>Using types to unit-test in Haskellhttps://lexi-lambda.github.io/blog/2016/10/03/using-types-to-unit-test-in-haskell/https://lexi-lambda.github.io/blog/2016/10/03/using-types-to-unit-test-in-haskell/03 Oct 2016<article><p>Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.</p><p>Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.</p><h2><a name="first-an-aside-on-testing-philosophy"></a>First, an aside on testing philosophy</h2><p>Testing methodology is a controversial topic within the larger programming community, and there are a multitude of different approaches. This blog post is about <em>unit testing</em>, an already nebulous term with a number of different definitions. For the purposes of this post, I will define a unit test as a test that stubs out collaborators of the code under test in some way. Accomplishing that in Haskell is what this is primarily about.</p><p>I want to be clear that I do not think that unit tests are the only way to write tests, nor the best way, nor even always an applicable way. Depending on your domain, rigorous unit testing might not even make sense, and other forms of tests (end-to-end, integration, benchmarks, etc.) might fulfill your needs.</p><p>In practice, though, implementing those other kinds of tests seems to be well-documented in Haskell compared to pure, object-oriented style unit testing. As my Haskell applications have grown, I have found myself wanting a more fine-grained testing tool that allows me to both test a piece of my codebase in isolation and also use my domain-specific types. This blog post is about that.</p><p>With that disclaimer out of the way, let’s talk about testing in Haskell.</p><h2><a name="drawing-seams-using-types"></a>Drawing seams using types</h2><p>One of the primary attributes of unit tests in object-oriented languages, especially statically-typed ones, is the concept of “seams” within a codebase. These are internal boundaries between components of a system. Some boundaries are obvious—interactions with a database, manipulation of the file system, and performing I/O over the network, to name a few examples—but others are more subtle. Especially in larger codebases, it can be helpful to isolate two related but distinct pieces of functionality as much as possible, which makes them easier to reason about, even if they’re actually part of the same codebase.</p><p>In OO languages, these seams are often marked using interfaces, whether explicitly (in the case of static languages) or implicitly (in the case of dynamic ones). By programming to an interface, it’s possible to create “fake” implementations of that interface for use in unit tests, effectively making it possible to stub out code that isn’t directly relevant to the code being tested.</p><p>In Haskell, representing these seams is a lot less obvious. Consider a fairly trivial function that reverses a file’s contents on the file system:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span></code></pre><p>This function is impossible to test without testing against a real file system. It simply performs I/O directly, and there’s no way to “mock out” the file system for testing purposes. Now, admittedly, this function is so trivial that a unit test might not seem worth the cost, but consider a slightly more complicated function that interacts with a database:</p><pre><code class="pygments"><span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>It might now be a bit more clear that it could be useful to test the above function without running a real database and doing all the necessary context setup before each test case. Indeed, it would be nice if a test could just provide stubbed implementations for <code>fetchUser</code> and <code>fetchRecentPosts</code>, then make assertions about the output.</p><p>One way to solve this problem is to pass the results of those two functions to <code>renderUserProfile</code> as arguments, turning it into a pure function that could be easily tested. This becomes obnoxious for functions of even just slightly more complexity, though (it is not unreasonable to imagine needing a handful of different queries to render a user’s profile page), and it requires significantly restructuring code simply because the tests need it.</p><p>The above code is not only difficult to test, however—it has another problem, too. Specifically, both functions return <code>IO</code> values, which means they can effectively do <em>anything</em>. Haskell has a very strong type system for typing terms, but it doesn’t provide any guarantees about effects beyond a simple yes/no answer about function purity. Even though the <code>renderUserProfile</code> function should really only need to interact with the database, it could theoretically delete files, send emails, make HTTP requests, or do any number of other things.</p><p>Fortunately, it’s possible to solve <em>both</em> problems—a lack of testability and a lack of type safety—using the same general technique. This approach is reminiscent of the interface-based seams of object-oriented languages, but unlike most object-oriented approaches, it provides additional type safety guarantees without the need to explicitly modify the code to support some kind of dependency injection.</p><h3><a name="making-implicit-interfaces-explicit"></a>Making implicit interfaces explicit</h3><p>Statically typed, object-oriented languages provide interfaces as a language construct to encode certain kinds of contracts into the type system, and Haskell has something similar. Typeclasses are, in many ways, an analog to OO interfaces, and they can be used in a similar way. In the above case, let’s write down interfaces that the <code>reverseFile</code> and <code>renderUserProfile</code> functions can use:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Monad</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span></code></pre><p>The really nice thing about these interfaces is that our function implementations don’t have to change <em>at all</em> to take advantage of them. In fact, all we have to change is their types:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">reverseFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="p">(</span><span class="n">reverse</span><span class="w"> </span><span class="n">contents</span><span class="p">)</span> + +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">HTML</span> +<span class="nf">renderUserProfile</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="n">userId</span> +<span class="w"> </span><span class="n">posts</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="n">userId</span> + +<span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">div</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="p">(</span><span class="n">userName</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"’s Profile"</span><span class="p">)</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">h2</span><span class="w"> </span><span class="s">"Recent Posts"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">(</span><span class="n">map</span><span class="w"> </span><span class="p">(</span><span class="n">li</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">postTitle</span><span class="p">)</span><span class="w"> </span><span class="n">posts</span><span class="p">)</span> +<span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty neat, since we haven’t had to alter our code at all, but we’ve managed to completely decouple ourselves from <code>IO</code>. This has the direct effect of both making our code more abstract (we no longer rely on the “real” file system or a “real” database, which makes our code easier to test) and restricting what our functions can do (just from looking at the type signatures, we know what side-effects they can perform).</p><p>Of course, since we’re now coding against an interface, our code doesn’t actually do much of anything. If we want to actually use the functions we’ve written, we’ll have to define instances of <code>MonadFS</code> and <code>MonadDB</code>. When actually running our code, we’ll probably still use <code>IO</code> (or some monad transformer stack with <code>IO</code> at the bottom), so we can define trivial instances for that existing use case:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">readFile</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Prelude</span><span class="o">.</span><span class="n">writeFile</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchUser</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SQL</span><span class="o">.</span><span class="n">fetchRecentPosts</span></code></pre><p>Even if we go no further, <strong>this is already incredibly useful</strong>. By restricting the sorts of effects our functions can perform at the type level, it becomes a lot easier to see which code is interacting with what. This can be invaluable when working in a part of a moderately large codebase that you are unfamiliar with. Even if the only instance of these typeclasses is <code>IO</code>, the benefits are immediately apparent.</p><p>Of course, this blog post is about testing, so we’re going to go further and take advantage of these seams we’ve now drawn. The question is: how?</p><h2><a name="testing-with-typeclasses-an-initial-attempt"></a>Testing with typeclasses: an initial attempt</h2><p>Given that we now have functions depending on an interface instead of <code>IO</code>, we can create separate instances of our typeclasses for use in tests. Let’s start with the <code>renderUserProfile</code> function. We’ll create a simple wrapper around the <code>Identity</code> type, since we don’t actually care much about the “effects” of our <code>MonadDB</code> methods:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Functor.Identity</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">)</span> + +<span class="nf">unTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Identity</span><span class="w"> </span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now, we’ll create a trivial instance of <code>MonadDB</code> for <code>TestM</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadDB</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">fetchUser</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Alyssa"</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="n">fetchRecentPosts</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span> +<span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="kt">Post</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">postTitle</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span></code></pre><p>With this instance, it’s now possible to write a simple unit test of the <code>renderUserProfile</code> function that doesn’t need a real database running at all:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"renderUserProfile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows the user’s name"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">h1</span><span class="w"> </span><span class="s">"Alyssa’s Profile"</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"shows a list of the user’s posts"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">unTestM</span><span class="w"> </span><span class="p">(</span><span class="n">renderUserProfile</span><span class="w"> </span><span class="p">(</span><span class="n">intToId</span><span class="w"> </span><span class="mi">1234</span><span class="p">))</span> +<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="p">`</span><span class="n">shouldContainElement</span><span class="p">`</span><span class="w"> </span><span class="n">ul</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="n">li</span><span class="w"> </span><span class="s">"Metacircular Evaluator"</span><span class="w"> </span><span class="p">]</span></code></pre><p>This is pretty nice, and running the above tests reveals a nice property of these kinds of isolated test cases: the test suite runs <em>really, really fast</em>. Communicating with a database, even in extremely simple ways, takes a measurable amount of time, especially with dozens of tests. In contrast, even with hundreds of tests, our unit test suite runs in less than a tenth of a second.</p><p>This all seems to be successful, so let’s try and apply the same testing technique to <code>reverseFile</code>.</p><h3><a name="testing-side-effectful-code"></a>Testing side-effectful code</h3><p>Looking at the type signature for <code>reverseFile</code>, we have a small problem:</p><pre><code class="pygments"><span class="nf">reverseFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>Specifically, the return type is <code>()</code>. Making any assertions against the result of this function would be completely worthless, given that it’s guaranteed to be the same exact thing each time. Instead, <code>reverseFile</code> is inherently side-effectful, so we want to be able to test that it properly interacts with the file system in the correct way.</p><p>In order to do this, a simple wrapper around <code>Identity</code> won’t be enough, but we can replace it with something more powerful: <code>Writer</code>. Specifically, we can use a writer monad to “log” what gets called in order to test side-effects. We’ll start by creating a new <code>TestM</code> type, just like last time:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">])</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span></code></pre><p>Using this slightly more powerful type, we can write a useful instance of <code>MonadFS</code> that will track the argument given to <code>writeFile</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span></code></pre><p>Again, the instance is quite simple, but it now enables us to write a straightforward unit test for <code>reverseFile</code>:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span></code></pre><p>Again, quite simple to both implement and use, and the test itself is blindingly fast. There’s another problem, though, which is that we have technically left part of <code>reverseFile</code> untested: we’ve completely ignored the <code>path</code> argument.</p><p>In this contrived example, it may seem silly to test something so trivial, but in real code, it’s quite possible that one would care very much about testing multiple different aspects about a single function. When testing <code>renderUserProfile</code>, this was not hard, since we could reuse the same <code>TestM</code> type and <code>MonadDB</code> instance for both test cases, but in the <code>reverseFile</code> example, we’ve ignored the path entirely.</p><p>We <em>could</em> adjust our <code>MonadFS</code> instance to also track the path provided to each method, but this has a few problems. First, it means every test case would depend on all the various properties we are testing, which would mean updating every test case when we add a new one. It would also be simply impossible if we needed to track multiple types—in this particular case, it turns out that <code>String</code> and <code>FilePath</code> are actually the same type, but in practice, there may be a handful of disparate, incompatible types.</p><p>Both of the above issues could be fixed by creating a sum type and manually filtering out the relevant elements in each test case, but a much more intuitive approach would be to simply have a separate instance for each case. Unfortunately, in Haskell, creating a new instance means creating an entirely new type. To illustrate how much duplication that would entail, we could create the following type and instance for testing proper propagation of the <code>path</code> argument:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">])</span> + +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="n">w</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="n">w</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="kt">TestM&#39;</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span></code></pre><p>Now it’s possible to add an extra test case that asserts that the proper path is provided to the two filesystem functions:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM&#39;</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This works, but it’s ultimately unacceptably complicated. Our test harness code is now significantly larger than the actual tests themselves, and the amount of boilerplate is frustrating. Verbose test suites are especially bad, since forcing programmers to jump through hoops just to implement a single test reduces the likelihood that people will actually write good tests, if they write tests at all. In contrast, if writing tests is easy, then people will naturally write more of them.</p><p>The above strategy to writing tests is not good enough, but it does reveal a particular problem: in Haskell, typeclass instances are not first-class values that can be manipulated and abstracted over, they are static constructs that can only be managed by the compiler, and users do not have a direct way to modify them. With some cleverness, however, we can actually create an approximation of first-class typeclass dictionaries, which will allow us to dramatically simplify the above testing mechanism.</p><h2><a name="creating-first-class-typeclass-instances"></a>Creating first-class typeclass instances</h2><p>In order to provide an easy way to construct instances, we need a way to represent instances as ordinary Haskell values. This is not terribly difficult, given that instances are conceptually just records containing a collection of functions. For example, we could create a datatype that represents an instance of the <code>MonadFS</code> typeclass:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>To avoid namespace clashes with the actual method identifiers, the record fields are prefixed with an underscore, but otherwise, the translation is remarkably straightforward. Using this record type, we can easily create values that represent the two instances we defined above:</p><pre><code class="pygments"><span class="nf">contentInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">String</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> + +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>These two values represent two different implementations of <code>MonadFS</code>, but since they’re ordinary Haskell values, they can be manipulated and even <em>extended</em> like any other records. This can be extremely useful, since it makes it possible to create a sort of “base” instance, then have individual test cases override individual pieces of functionality piecemeal.</p><p>Of course, although we’ve written these two instances, we have no way to actually use them. After all, Haskell does not provide a way to explicitly provide typeclass dictionaries. Fortunately, we can create a sort of “proxy” type that will use a reader to thread the dictionary around explicitly, and the instance can defer to the dictionary’s implementation.</p><h3><a name="creating-an-instance-proxy"></a>Creating an instance proxy</h3><p>To represent our proxy type, we’ll use a combination of a <code>Writer</code> and a <code>ReaderT</code>; the former to implement the logging used by instances, and the latter to actually thread around the dictionary. Our type will look like this:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span> +<span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="p">(</span><span class="kt">ReaderT</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Writer</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Functor</span><span class="p">,</span><span class="w"> </span><span class="kt">Applicative</span><span class="p">,</span><span class="w"> </span><span class="kt">Monad</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadReader</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">))</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">MonadWriter</span><span class="w"> </span><span class="n">log</span> +<span class="w"> </span><span class="p">)</span> + +<span class="nf">logTestM</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span> +<span class="nf">logTestM</span><span class="w"> </span><span class="n">inst</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">execWriter</span><span class="w"> </span><span class="p">(</span><span class="n">runReaderT</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">inst</span><span class="p">)</span></code></pre><p>This might look rather complicated, and it is, but let’s break down exactly what it’s doing.</p><ol><li><p>The <code>TestM</code> type includes two type parameters. The first is the type of value that will be logged (hence the name <code>log</code>), which corresponds to the argument to <code>Writer</code> from previous incarnations of <code>TestM</code>. Unlike those types, though, we want this version to work with any <code>Monoid</code>, so we’ll make it a type parameter. The second parameter is simply the type of the current monadic value, as before.</p></li><li><p>The type itself is defined as a wrapper around a small monad transformer stack, the first of which is <code>ReaderT</code>. The state threaded around by the reader is, in this case, the instance dictionary, which is <code>MonadFSInst</code>.</p><p>However, recall that <code>MonadFSInst</code> accepts a type variable—the type of a monad itself—so we must provide <code>TestM log</code> as an argument to <code>MonadFSInst</code>. This slight bit of indirection allows us to tie the knot between the mutually dependent instances and proxy type.</p></li><li><p>The base monad in the transformer stack is <code>Writer</code>, which is used to actually implement the logging functionality, just like in prior cases. The only difference now is that the <code>log</code> type parameter now determines what the writer actually produces.</p></li><li><p>Finally, as before, we use <code>GeneralizedNewtypeDeriving</code> to derive all the relevant <code>mtl</code> classes, adding the somewhat wordy <code>MonadReader</code> constraint to the list.</p></li></ol><p>Using this single type, we can now implement a <code>MonadFS</code> instance that defers to the dictionary carried around within <code>TestM</code>’s reader state:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Monoid</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">MonadFS</span><span class="w"> </span><span class="p">(</span><span class="kt">TestM</span><span class="w"> </span><span class="n">log</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">readFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_readFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="n">writeFile</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">asks</span><span class="w"> </span><span class="n">_writeFile</span> +<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="n">contents</span></code></pre><p>This may seem somewhat boilerplate-y, and it is to some extent, but the important consideration is that this boilerplate only needs to be written <em>once</em>. With this in place, it’s now possible to write an arbitrary number of first-class instances that use the above mechanism without extending the mechanism at all.</p><p>To see what actually using this code would look like, let’s update the <code>reverseFile</code> tests to use the new <code>TestM</code> implementation, as well as the <code>contentInst</code> and <code>pathInst</code> dictionaries from earlier:</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>We can do a little bit better, though. Really, the definitions of <code>contentInst</code> and <code>pathInst</code> are specific to each test case. With ordinary typeclass instances, we cannot scope them to any particular block, but since <code>MonadFSInst</code> is just an ordinary Haskell datatype, we can manipulate them just like any other Haskell values. Therefore, we can just inline those instances’ definitions into the test cases themselves to keep them closer to the actual tests.</p><pre><code class="pygments"><span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">contents</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">tell</span><span class="w"> </span><span class="p">[</span><span class="n">path</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestM</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p>This is pretty good. We’re now able to create inline instances of our <code>MonadFS</code> typeclass, which allows us to write extremely concise unit tests using ordinary Haskell typeclasses as system seams. We’ve managed to cut down on the boilerplate considerably, though we still have a couple problems. For one, this example only uses a single typeclass containing only two methods. A real <code>MonadFS</code> typeclass would likely have at least a dozen methods for performing various filesystem operations, and writing out the instance dictionaries for every single method, even the ones that aren’t used within the code under test, would be pretty frustratingly verbose.</p><p>This problem is solvable, though. Since instances are just ordinary Haskell records, we can create a “base” instance that just throws an exception whenever the method is called:</p><pre><code class="pygments"><span class="nf">baseInst</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">MonadFSInst</span><span class="w"> </span><span class="n">m</span> +<span class="nf">baseInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">MonadFSInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_readFile’"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"unimplemented instance method ‘_writeFile’"</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Then code that only uses <code>readFile</code> could only override that particular method, for example:</p><pre><code class="pygments"><span class="kr">let</span><span class="w"> </span><span class="n">myInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">baseInst</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span></code></pre><p>Normally, of course, this would be a terrible idea. However, since this is all just test code, it can be extremely useful in quickly figuring out what methods need to be stubbed out for a particular test case. Since all the code actually gets run at test time, attempts to use unimplemented instance methods will immediately raise an error, informing the programmer which methods need to be implemented to make the test pass. This can also help to significantly cut down on the amount of effort it takes to implement each test.</p><p>Another problem is that our approach is specialized exclusively to <code>MonadFS</code>. What about functions that use both <code>MonadFS</code> <em>and</em> <code>MonadDB</code>, for example? Fortunately, that is not hard to solve, either. We can adapt the <code>MonadFSInst</code> type to include fields for all of the typeclasses relevant to our system, turning it into a generic test fixture of sorts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">FixtureInst</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">FixtureInst</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">-- MonadFS</span> +<span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">String</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">FilePath</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span> + +<span class="w"> </span><span class="c1">-- MonadDB</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchUser</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="kt">User</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_fetchRecentPosts</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Id</span><span class="w"> </span><span class="kt">User</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">[</span><span class="kt">Post</span><span class="p">]</span> +<span class="w"> </span><span class="p">}</span></code></pre><p>Updating <code>TestM</code> to use <code>FixtureInst</code> instead of <code>MonadFSInst</code> is trivial, and all the rest of the infrastructure still works. However, this means that every time a new typeclass is added, three things need to be updated:</p><ol><li><p>Its methods need to be added to the <code>FixtureInst</code> record.</p></li><li><p>Those methods need to be given error-raising defaults in the <code>baseInst</code> value.</p></li><li><p>An actual instance of the typeclass needs to be written for <code>TestM</code> that defers to the <code>FixtureInst</code> value.</p></li></ol><p>Furthermore, most of this manual manipulation of methods is required every time a particular typeclass changes, whether that means adding a method, removing a method, renaming a method, or changing a method’s type. This is especially frustrating given that all this code is really just mechanical boilerplate that could all be derived by the set of typeclasses being tested.</p><p>That last point is especially important: aside from the instances themselves, every piece of boilerplate above is obviously possible to generate from existing types alone. With that piece of information in mind, we can do even better: we can use Template Haskell.</p><h2><a name="removing-the-boilerplate-using-test-fixture"></a>Removing the boilerplate using <code>test-fixture</code></h2><p>The above code was not only rather boilerplate-heavy, it was pretty complicated. Fortunately, you don’t actually have to write it. Enter the library <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code></a>:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture</span> +<span class="kr">import</span><span class="w"> </span><span class="nn">Control.Monad.TestFixture.TH</span> + +<span class="nf">mkFixture</span><span class="w"> </span><span class="s">"FixtureInst"</span><span class="w"> </span><span class="p">[</span><span class="kt">&#39;&#39;MonadFS</span><span class="p">,</span><span class="w"> </span><span class="kt">&#39;&#39;MonadDB</span><span class="p">]</span> + +<span class="nf">spec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="s">"reverseFile"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"reverses a file’s contents on the filesystem"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">contentInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">"hello"</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="kr">_</span><span class="w"> </span><span class="n">contents</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">contents</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">contentInst</span> +<span class="w"> </span><span class="n">calls</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"olleh"</span><span class="p">]</span> + +<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="s">"operates on the file at the provided path"</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">pathInst</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">def</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">_readFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span><span class="w"> </span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">return</span><span class="w"> </span><span class="s">""</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">_writeFile</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nf">\</span><span class="n">path</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">log</span><span class="w"> </span><span class="n">path</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">logTestFixture</span><span class="w"> </span><span class="p">(</span><span class="n">reverseFile</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">)</span><span class="w"> </span><span class="n">pathInst</span> +<span class="w"> </span><span class="n">paths</span><span class="w"> </span><span class="p">`</span><span class="n">shouldBe</span><span class="p">`</span><span class="w"> </span><span class="p">[</span><span class="s">"foo.txt"</span><span class="p">,</span><span class="w"> </span><span class="s">"foo.txt"</span><span class="p">]</span></code></pre><p><strong>That’s it.</strong> The above code automatically generates everything you need to write fast, simple, deterministic unit tests in Haskell. The <code>mkFixture</code> function is a Template Haskell macro that expands into a definition quite similar to the <code>FixtureInst</code> type we wrote by hand, but since it’s automatically generated from the typeclass definitions, it never needs to be updated.</p><p>The <code>logTestFixture</code> function replaces the <code>logTestM</code> function we wrote by hand, but it works exactly the same. The <code>Control.Monad.TestFixture</code> library also exports a <code>log</code> function that is a synonym for <code>tell . singleton</code>, but using <code>tell</code> directly still works if you prefer.</p><p>The <code>mkFixture</code> function also generates a <code>Default</code> instance, which replaces the <code>baseInst</code> value defined earlier. It functions the same way, though, producing useful error messages that refer to the names of unimplemented typeclass methods that have not been stubbed out.</p><p>This blog post is not a <code>test-fixture</code> tutorial—indeed, it is much more complicated than a <code>test-fixture</code> tutorial would be, since it covers what the library is really doing under the hood—but if you’re interested, I would highly recommend you take a look at the <a href="http://hackage.haskell.org/package/test-fixture"><code>test-fixture</code> documentation on Hackage</a>.</p><h2><a name="conclusion-credits-and-similar-techniques"></a>Conclusion, credits, and similar techniques</h2><p>This blog post came about as the result of a need my coworkers and I found when writing Haskell code; we wanted a way to write unit tests quickly and easily, but we didn’t find much advice from the rest of the Haskell ecosystem. The <code>test-fixture</code> library is the result of that exploratory work, and we currently use it to test a significant portion of our Haskell code.</p><p>It would be extremely unfair to suggest that I was the inventor of this technique or the inventor of the library. Two of my coworkers, <a href="https://github.com/jxv">Joe Vargas</a> and <a href="https://github.com/aztecrex">Greg Wiley</a>, came up with the general approach and wrote <code>Control.Monad.TestFixture</code>, and I simply wrote the Template Haskell macro to eliminate the boilerplate. With that in mind, I think I can say with some fairness that I think this technique is a joy to use when unit testing is a desirable goal, and I would definitely recommend it if you are interested in doing isolated testing in Haskell.</p><p>The general technique of using typeclasses to emulate effects was in part inspired by the well-known <code>mtl</code> library. An alternate approach to writing unit-testable Haskell code is using free monads, but overall, I prefer this approach over free monads because the typeclass constraints add type safety in ways that free monads do not (at least not without additional boilerplate), and this approach also lends itself well to static analysis-based boilerplate reduction techniques. It has its own tradeoffs, though, so if you’ve had success with free monads, then I certainly make no claim this is a superior approach, just one that I’ve personally found pleasant.</p><p>As a final note, if you <em>do</em> check out <code>test-fixture</code>, feel free to leave feedback by opening issues on <a href="https://github.com/cjdev/test-fixture/issues">the GitHub issue tracker</a>—even things like confusing documentation are worth a bug report.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/typed-racket.atom.xml b/feeds/typed-racket.atom.xml new file mode 100644 index 0000000..9113103 --- /dev/null +++ b/feeds/typed-racket.atom.xml @@ -0,0 +1,107 @@ +Posts tagged ‘typed racket’ | Alexis King’s Blog2015-12-21T00:00:00ZADTs in Typed Racket with macros2015-12-21T00:00:00Z2015-12-21T00:00:00ZAlexis King<article><p>Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate <em>why</em> macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is <strong>ADTs</strong>.</p><h2><a name="warning-this-is-not-a-macro-tutorial"></a>Warning: this is not a macro tutorial</h2><p>First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you <em>are</em> interested in learning about macros, I must recommend Greg Hendershott's <a href="http://www.greghendershott.com/fear-of-macros/">Fear of Macros</a>. It is good. This is not that.</p><p>Now, with that out of the way, let's get started.</p><h2><a name="what-we-re-building"></a>What we&rsquo;re building</h2><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data types</a>, or <em>ADTs</em>, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.</p><p>Racket also already has a facility for creating custom data structures in the form of <em>structs</em>, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.</p><p>With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="kt">Tree</span></code></pre><p>This already demonstrates a few of the core things we'll need to build:</p><ol><li><p>Each ADT has a <em>data type</em>, in this case <code>Tree</code>. This name only exists in the world of types, it isn't a value.</p></li><li><p>Each ADT has various <em>data constructors</em>, in this case <code>Leaf</code> and <code>Node</code>.</p></li><li><p>Each data constructor may accept any number of arguments, each of which have a specific type.</p></li><li><p>The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.</p></li></ol><p>Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>With this in mind, we can add a fifth and final point to our list:</p><ol start="5"><li><p>ADTs must be able to be parametrically polymorphic.</p></li></ol><p>That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.</p><h3><a name="describing-adts-in-racket"></a>Describing ADTs in Racket</h3><p>How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>This looks pretty good. Just like with the Haskell implementation, <code>Tree</code> should only exist at the type level, and <code>Empty</code>, <code>Leaf</code>, and <code>Node</code> should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be <code>(Leaf 7)</code>.</p><p>Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's <code>match</code>[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">tree-sum</span><span class="w"> </span><span class="p">((</span><span class="n">Tree</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">tree</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">tree</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Node</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">l</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">r</span><span class="p">))]))</span></code></pre><p>Given that Racket's <code>struct</code> form automatically produces identifiers that cooperate with <code>match</code>, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.</p><h2><a name="implementing-adts-as-syntax"></a>Implementing ADTs as syntax</h2><p>Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. The <code>syntax/parse</code> library works like the traditional Scheme <code>syntax-case</code> on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.</p><p>Since this is not a macro tutorial, the following implementation assumes you already know how to use <code>syntax/parse</code>. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.</p><h3><a name="parsing-types-with-a-syntax-class"></a>Parsing types with a syntax class</h3><p>To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like <code>Tree</code>, or they can be identifiers with parameters, like <code>(Tree a)</code>. We'll want to cover both cases.</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">))))</span></code></pre><p>This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (<code>...+</code>) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use <code>#:attr</code> to give the <code>param</code> attribute the default value of an empty list, so this syntax class will actually <em>normalize</em> the input we get in addition to actually parsing it.</p><h3><a name="a-first-attempt-at-define-datatype"></a>A first attempt at <code>define-datatype</code></h3><p>Now we can move on to actually implementing <code>define-datatype</code>. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using <code>syntax-parser</code>, which actually does the parsing for our macro.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">]))</span></code></pre><p>This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with <code>_</code> (which will just be the identifier <code>define-datatype</code>), then expecting a <code>type-name</code>, which uses the <code>type</code> syntax class we defined above. Next, we expect zero or more <code>data-constructor</code>s, which also use the <code>type</code> syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.</p><p>Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">([</span><span class="n">f</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))]))</span></code></pre><p>This is actually really close to being correct. This will generate a struct definition for each <code>data-constructor</code>, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have <em>names</em>, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for <em>all</em> the fields, <code>f</code>, so if any data constructor has two or more fields, we'll get an error.</p><p>Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called <code>generate-temporary</code>, which generates random identifiers. Our next attempt might look like this:</p><pre><code class="pygments"><span class="o">#`</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="o">#,</span><span class="p">(</span><span class="n">generate-temporary</span><span class="p">)</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>The <code>#,</code> lets us "escape" from the template to execute <code>(generate-temporary)</code> and interpolate its result into the syntax. Unfortunately, this doesn't work. We <em>do</em> generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.</p><h3><a name="more-leveraging-syntax-classes"></a>More leveraging syntax classes</h3><p>As it turns out, this is <em>also</em> easy to do with syntax classes. We can add an extra attribute to our <code>type</code> syntax class to generate a random identifier with each one. Again, we can use <code>#:attr</code> to do that automatically. Our new definition for <code>type</code> will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span></code></pre><p>Here we're using <code>generate-temporaries</code> instead of <code>generate-temporary</code>, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each <code>param</code>.</p><p>We can now fix our macro to use this <code>field-id</code> attribute instead of the static field name:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><h3><a name="creating-the-supertype"></a>Creating the supertype</h3><p>We're almost done—now we just need to implement our overall type, the one defined by <code>type-name</code>. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">Tree</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">Leaf</span><span class="w"> </span><span class="n">Node</span><span class="p">))</span></code></pre><p>However, a polymorphic type alias would need to include the type parameters in each subtype, like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using <strong>procedural macros</strong>.</p><p>To build each properly-instantiated type, we'll use a combination of <code>define/with-syntax</code> and Racket's list comprehensions, <code>for/list</code>. The <code>define/with-syntax</code> form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by <code>syntax-parser</code>. This will allow us to break up our result into multiple steps. Technically, <code>define/with-syntax</code> is not strictly necessary—we could just use <code>#`</code> and <code>#,</code>—but it's cleaner to work with.</p><p>We'll start by defining a set of instantiated data constructor types, one per <code>data-constructor</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span></code></pre><p>Now with our definition for <code>data-type</code>, we can implement our type alias for the supertype extremely easily:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><h3><a name="putting-it-all-together"></a>Putting it all together</h3><p>There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by <code>type-name</code> are in scope for each data constructor's structure definition. We can do this by making use of <code>type-name.param</code> within each produced struct definition, resulting in this:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><p>And we're done! The final macro, now completed, looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span> + +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span><span class="w"> </span><span class="k">...</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">)))]))</span></code></pre><p>It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.</p><h2><a name="using-our-adts"></a>Using our ADTs</h2><p>With the macro built, we can now actually use our ADTs using the syntax we described! The following is now <em>valid code</em>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span> +<span class="nb">-</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">Positive-Byte</span><span class="p">)</span> +<span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span></code></pre><p>We can use this to define common data types, such as Haskell's <code>Maybe</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Nothing</span><span class="p">)</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-default</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-default</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-then</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-then</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Nothing</span><span class="p">)]))</span></code></pre><p>And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="n">Expr</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="n">Number</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Expr</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Number</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">e</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Value</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Add</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">-</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Divide</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">/</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">7</span><span class="p">))))</span> +<span class="mi">4</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, <a href="https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e">I've put together a gist here</a>.</p><h2><a name="conclusions-and-credit"></a>Conclusions and credit</h2><p>This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly <em>are</em> powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.</p><p>This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.</p><p>That said, I think it's pretty cool.</p><p>Finally, I must give credit where credit is due. Thanks to <a href="http://andmkent.com">Andrew M. Kent</a> for the creation of the <a href="https://github.com/andmkent/datatype">datatype</a> package, which served as the inspiration for this blog post. Many thanks to <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> and <a href="http://www.ccs.neu.edu/home/matthias/">Matthias Felleisen</a> for their work on creating <code>syntax/parse</code>, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.</p><p>Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/typed-racket.rss.xml b/feeds/typed-racket.rss.xml new file mode 100644 index 0000000..713a7c2 --- /dev/null +++ b/feeds/typed-racket.rss.xml @@ -0,0 +1,107 @@ +Posts tagged ‘typed racket’ | Alexis King’s BlogPosts tagged ‘typed racket’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/typed-racket.html21 Dec 201521 Dec 201560ADTs in Typed Racket with macroshttps://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/https://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/21 Dec 2015<article><p>Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate <em>why</em> macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is <strong>ADTs</strong>.</p><h2><a name="warning-this-is-not-a-macro-tutorial"></a>Warning: this is not a macro tutorial</h2><p>First, a disclaimer: this post assumes at least some knowledge of Scheme/Racket macros. Ideally, you would be familiar with Racket itself. But if you aren't, fear not: if you get lost, don't worry. Hold on to the bigger picture, and you'll likely learn more than someone who knows enough to follow all the way through. If you <em>are</em> interested in learning about macros, I must recommend Greg Hendershott's <a href="http://www.greghendershott.com/fear-of-macros/">Fear of Macros</a>. It is good. This is not that.</p><p>Now, with that out of the way, let's get started.</p><h2><a name="what-we-re-building"></a>What we&rsquo;re building</h2><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data types</a>, or <em>ADTs</em>, are a staple of the ML family of functional programming languages. I won't go into detail here—I want to focus on the implementation—but they're a very descriptive way of modeling data that encourages designing functions in terms of pattern-matching, something that Racket is already good at.</p><p>Racket also already has a facility for creating custom data structures in the form of <em>structs</em>, which are extremely flexible, but also a little verbose. Racket structs are more powerful than we need, but that means we can implement our ADTs in terms of Racket's struct system.</p><p>With that in mind, what should our syntax look like? Well, let's consider a quintessential example of ADTs: modeling a simple tree. For now, let's just consider a tree of integers. For reference, the Haskell syntax for such a data structure would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="kt">Tree</span></code></pre><p>This already demonstrates a few of the core things we'll need to build:</p><ol><li><p>Each ADT has a <em>data type</em>, in this case <code>Tree</code>. This name only exists in the world of types, it isn't a value.</p></li><li><p>Each ADT has various <em>data constructors</em>, in this case <code>Leaf</code> and <code>Node</code>.</p></li><li><p>Each data constructor may accept any number of arguments, each of which have a specific type.</p></li><li><p>The types that data constructors may accept include the ADT's datatype itself—that is, definitions can be recursive.</p></li></ol><p>Of course, there's one more important feature we're missing: polymorphism. Our definition of a tree is overly-specific, and really, it should be able to hold any kind of data, not just integers. In Haskell, we can do that by adding a type parameter:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Empty</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Node</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>With this in mind, we can add a fifth and final point to our list:</p><ol start="5"><li><p>ADTs must be able to be parametrically polymorphic.</p></li></ol><p>That covers all of our requirements for basic ADTs. Now we're ready to port this idea to Racket.</p><h3><a name="describing-adts-in-racket"></a>Describing ADTs in Racket</h3><p>How should we take the Haskell syntax for an ADT definition and adapt it to Racket's parenthetical s-expressions? By taking some cues from the Haskell implementation, Typed Racket's type syntax, and Racket's naming conventions, a fairly logical syntax emerges:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>This looks pretty good. Just like with the Haskell implementation, <code>Tree</code> should only exist at the type level, and <code>Empty</code>, <code>Leaf</code>, and <code>Node</code> should be constructor functions. Our syntax mirrors Racket function application, too—the proper way to create a leaf would be <code>(Leaf 7)</code>.</p><p>Now that we can create ADT values, how should we extract the values from them? Well, just like in ML-likes, we can use pattern-matching. We don't need to reinvent the wheel for this one; we should be able to just use Racket's <code>match</code>[racket] with our datatypes. For example, a function that sums all the values in a tree might look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">tree-sum</span><span class="w"> </span><span class="p">((</span><span class="n">Tree</span><span class="w"> </span><span class="n">Integer</span><span class="p">)</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Integer</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">tree</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">tree</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Node</span><span class="w"> </span><span class="n">l</span><span class="w"> </span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">l</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">tree-sum</span><span class="w"> </span><span class="n">r</span><span class="p">))]))</span></code></pre><p>Given that Racket's <code>struct</code> form automatically produces identifiers that cooperate with <code>match</code>, this shouldn't be hard at all. And with our syntax settled, we're ready to begin implementation.</p><h2><a name="implementing-adts-as-syntax"></a>Implementing ADTs as syntax</h2><p>Now for the fun part. To implement our ADT syntax, we'll employ Racket's industrial-strength macro DSL, <a href="http://docs.racket-lang.org/syntax/stxparse.html"><code>syntax/parse</code></a>. The <code>syntax/parse</code> library works like the traditional Scheme <code>syntax-case</code> on steroids, and one of the most useful features is the ability to define "syntax classes" that encapsulate reusable parsing rules into declarative components.</p><p>Since this is not a macro tutorial, the following implementation assumes you already know how to use <code>syntax/parse</code>. However, all of the concepts here are well within the reaches of any intermediate macrologist, so don't be intimidated by some of the more complex topics at play.</p><h3><a name="parsing-types-with-a-syntax-class"></a>Parsing types with a syntax class</h3><p>To implement ADTs, we're going to want to define exactly one syntax class, a class that describes the grammar for a type. As we've seen, types can be bare identifiers, like <code>Tree</code>, or they can be identifiers with parameters, like <code>(Tree a)</code>. We'll want to cover both cases.</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span><span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">))))</span></code></pre><p>This syntax class has two rules, one that's a bare identifier, and one that's a list. The ellipsis followed by a plus (<code>...+</code>) in the second example means "one or more", so parsing those parameters will automatically be handled for us. In the bare identifier example, we use <code>#:attr</code> to give the <code>param</code> attribute the default value of an empty list, so this syntax class will actually <em>normalize</em> the input we get in addition to actually parsing it.</p><h3><a name="a-first-attempt-at-define-datatype"></a>A first attempt at <code>define-datatype</code></h3><p>Now we can move on to actually implementing <code>define-datatype</code>. The rules are simple: we need to generate a structure type for each one of the data constructors, and we need to generate a type definition for the parent type itself. This is pretty simple to implement using <code>syntax-parser</code>, which actually does the parsing for our macro.</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">]))</span></code></pre><p>This definition will do all the parsing we need. It parses the entire macro "invocation", ignoring the first datum with <code>_</code> (which will just be the identifier <code>define-datatype</code>), then expecting a <code>type-name</code>, which uses the <code>type</code> syntax class we defined above. Next, we expect zero or more <code>data-constructor</code>s, which also use the <code>type</code> syntax class. That's all we have to do for parsing. We now have all the information we need to actually output the expansion for the macro.</p><p>Of course, it won't be that easy: this is the difficult part. The first step is to generate a Racket struct for each data constructor. We can do this pretty easily with some simple use of Racket's syntax templating facility. A naïve attempt would look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">([</span><span class="n">f</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))]))</span></code></pre><p>This is actually really close to being correct. This will generate a struct definition for each <code>data-constructor</code>, where each struct has the name of the data constructor and the same number of fields as arguments provided. The trouble is that in Racket structs, all of the fields have <em>names</em>, but in our ADTs, all the fields are anonymous and by-position. Currently, we're just using the same name for <em>all</em> the fields, <code>f</code>, so if any data constructor has two or more fields, we'll get an error.</p><p>Since we don't care about the field names, what we want to do is just generate random names for every field. To do this, we can use a Racket function called <code>generate-temporary</code>, which generates random identifiers. Our next attempt might look like this:</p><pre><code class="pygments"><span class="o">#`</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="o">#,</span><span class="p">(</span><span class="n">generate-temporary</span><span class="p">)</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><p>The <code>#,</code> lets us "escape" from the template to execute <code>(generate-temporary)</code> and interpolate its result into the syntax. Unfortunately, this doesn't work. We <em>do</em> generate a random field name, but the ellipsis will re-use the same generated value when it repeats the fields, rendering our whole effort pointless. We need to generate the field names once per type.</p><h3><a name="more-leveraging-syntax-classes"></a>More leveraging syntax classes</h3><p>As it turns out, this is <em>also</em> easy to do with syntax classes. We can add an extra attribute to our <code>type</code> syntax class to generate a random identifier with each one. Again, we can use <code>#:attr</code> to do that automatically. Our new definition for <code>type</code> will look like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span></code></pre><p>Here we're using <code>generate-temporaries</code> instead of <code>generate-temporary</code>, which will conveniently generate a new identifier for each of the elements in the list we provide it. This way, we'll get a fresh identifier for each <code>param</code>.</p><p>We can now fix our macro to use this <code>field-id</code> attribute instead of the static field name:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><h3><a name="creating-the-supertype"></a>Creating the supertype</h3><p>We're almost done—now we just need to implement our overall type, the one defined by <code>type-name</code>. This is implemented as a trivial type alias, but we need to ensure that polymorphic types are properly handled. For example, a non-polymorphic type would need to be handled like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">Tree</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">Empty</span><span class="w"> </span><span class="n">Leaf</span><span class="w"> </span><span class="n">Node</span><span class="p">))</span></code></pre><p>However, a polymorphic type alias would need to include the type parameters in each subtype, like this:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span></code></pre><p>How can we do this? Well, so far, we've been very declarative by using syntax patterns, templates, and classes. However, this is a more pernicious problem to solve with our declarative tools. Fortunately, it's very easy to fall back to using <strong>procedural macros</strong>.</p><p>To build each properly-instantiated type, we'll use a combination of <code>define/with-syntax</code> and Racket's list comprehensions, <code>for/list</code>. The <code>define/with-syntax</code> form binds values to pattern identifiers, which can be used within syntax patterns just like the ones bound by <code>syntax-parser</code>. This will allow us to break up our result into multiple steps. Technically, <code>define/with-syntax</code> is not strictly necessary—we could just use <code>#`</code> and <code>#,</code>—but it's cleaner to work with.</p><p>We'll start by defining a set of instantiated data constructor types, one per <code>data-constructor</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">))</span></code></pre><p>Now we can fill in the body with any code we'd like, so long as each body returns a syntax object. We can use some trivial branching logic to determine which form we need:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span></code></pre><p>Now with our definition for <code>data-type</code>, we can implement our type alias for the supertype extremely easily:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">))</span></code></pre><h3><a name="putting-it-all-together"></a>Putting it all together</h3><p>There's just one more thing to do before we can call this macro finished: we need to ensure that all the type parameters defined by <code>type-name</code> are in scope for each data constructor's structure definition. We can do this by making use of <code>type-name.param</code> within each produced struct definition, resulting in this:</p><pre><code class="pygments"><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="n">data-constructor.name</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="k">...</span><span class="p">)</span></code></pre><p>And we're done! The final macro, now completed, looks like this:</p><pre><code class="pygments"><span class="p">(</span><span class="k">begin-for-syntax</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-syntax-class</span><span class="w"> </span><span class="n">type</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="n">name:id</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">param</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">()</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">&#39;</span><span class="p">())</span> +<span class="w"> </span><span class="p">(</span><span class="n">pattern</span><span class="w"> </span><span class="p">(</span><span class="n">name:id</span><span class="w"> </span><span class="n">param</span><span class="w"> </span><span class="n">...+</span><span class="p">)</span> +<span class="w"> </span><span class="kd">#:attr</span><span class="w"> </span><span class="p">[</span><span class="n">field-id</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">generate-temporaries</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">param</span><span class="w"> </span><span class="k">...</span><span class="p">)))))</span> + +<span class="p">(</span><span class="k">define-syntax</span><span class="w"> </span><span class="n">define-datatype</span> +<span class="w"> </span><span class="p">(</span><span class="n">syntax-parser</span> +<span class="w"> </span><span class="p">[(</span><span class="k">_</span><span class="w"> </span><span class="n">type-name:type</span><span class="w"> </span><span class="n">data-constructor:type</span><span class="w"> </span><span class="k">...</span><span class="p">)</span> + +<span class="w"> </span><span class="p">(</span><span class="n">define/with-syntax</span><span class="w"> </span><span class="p">[</span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">]</span> +<span class="w"> </span><span class="p">(</span><span class="k">for/list</span><span class="w"> </span><span class="p">([</span><span class="n">name</span><span class="w"> </span><span class="p">(</span><span class="nb">in-syntax</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">data-constructor.name</span><span class="w"> </span><span class="k">...</span><span class="p">))])</span> +<span class="w"> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stx-null?</span><span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))</span> +<span class="w"> </span><span class="n">name</span> +<span class="w"> </span><span class="o">#`</span><span class="p">(</span><span class="o">#,</span><span class="n">name</span><span class="w"> </span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">))))</span> + +<span class="w"> </span><span class="o">#&#39;</span><span class="p">(</span><span class="k">begin</span> +<span class="w"> </span><span class="p">(</span><span class="k">struct</span><span class="w"> </span><span class="p">(</span><span class="n">type-name.param</span><span class="w"> </span><span class="k">...</span><span class="p">)</span><span class="w"> </span><span class="n">data-constructor.name</span> +<span class="w"> </span><span class="p">([</span><span class="n">data-constructor.field-id</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="n">data-constructor.param</span><span class="p">]</span><span class="w"> </span><span class="k">...</span><span class="p">))</span><span class="w"> </span><span class="k">...</span> +<span class="w"> </span><span class="p">(</span><span class="n">define-type</span><span class="w"> </span><span class="n">type-name</span><span class="w"> </span><span class="p">(</span><span class="n">U</span><span class="w"> </span><span class="n">data-type</span><span class="w"> </span><span class="k">...</span><span class="p">)))]))</span></code></pre><p>It's a little bit dense, certainly, but it is not as complicated or scary as it might seem. It's a simple, mostly declarative, powerful way to transform a DSL into ordinary Typed Racket syntax, and now all we have to do is put it to use.</p><h2><a name="using-our-adts"></a>Using our ADTs</h2><p>With the macro built, we can now actually use our ADTs using the syntax we described! The following is now <em>valid code</em>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Empty</span> +<span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Tree</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span> +<span class="nb">-</span><span class="w"> </span><span class="n">:</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="n">Positive-Byte</span><span class="p">)</span> +<span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Node</span><span class="w"> </span><span class="p">(</span><span class="n">Empty</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Leaf</span><span class="w"> </span><span class="mi">7</span><span class="p">)))</span></code></pre><p>We can use this to define common data types, such as Haskell's <code>Maybe</code>:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="w"> </span><span class="n">Nothing</span><span class="p">)</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-default</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-default</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">v</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="n">v</span><span class="p">]))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">maybe-then</span><span class="w"> </span><span class="p">(</span><span class="n">All</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">maybe-then</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="n">f</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">m</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Just</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">f</span><span class="w"> </span><span class="n">a</span><span class="p">)]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Nothing</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Nothing</span><span class="p">)]))</span></code></pre><p>And of course, we can also use it to define ADTs that use concrete types rather that type parameters, if we so desire. This implements a small mathematical language, along with a trivial interpreter:</p><pre><code class="pygments"><span class="p">(</span><span class="n">define-datatype</span><span class="w"> </span><span class="n">Expr</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="n">Number</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="n">Expr</span><span class="w"> </span><span class="n">Expr</span><span class="p">))</span> + +<span class="p">(</span><span class="n">:</span><span class="w"> </span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Expr</span><span class="w"> </span><span class="k">-&gt;</span><span class="w"> </span><span class="n">Number</span><span class="p">))</span> +<span class="p">(</span><span class="k">define</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">e</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="k">match</span><span class="w"> </span><span class="n">e</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Value</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Add</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Subtract</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">-</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Multiply</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">*</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]</span> +<span class="w"> </span><span class="p">[(</span><span class="n">Divide</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nb">/</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="n">b</span><span class="p">))]))</span> + +<span class="nb">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">evaluate</span><span class="w"> </span><span class="p">(</span><span class="n">Add</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="n">Multiply</span><span class="w"> </span><span class="p">(</span><span class="n">Divide</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span> +<span class="w"> </span><span class="p">(</span><span class="n">Value</span><span class="w"> </span><span class="mi">7</span><span class="p">))))</span> +<span class="mi">4</span><span class="w"> </span><span class="m">1/2</span></code></pre><p>There's all the power of ADTs, right in Racket, all implemented in 22 lines of code. If you'd like to see all the code together in a runnable form, <a href="https://gist.github.com/lexi-lambda/18cf7a9156f743a1317e">I've put together a gist here</a>.</p><h2><a name="conclusions-and-credit"></a>Conclusions and credit</h2><p>This isn't the simplest macro to create, nor is it the most complex. The code examples might not even make much sense until you try it out yourself. Macros, like any difficult concept, are not always easy to pick up, but they certainly <em>are</em> powerful. The ability to extend the language in such a way, in the matter of minutes, is unparalleled in languages other than Lisp.</p><p>This is, of course, a blessing and a curse. Lisps reject some of the syntactic landmarks that often aid in readability for the power to abstract programs into their bare components. In the end, is this uniform conciseness more or less readable? That's an incredibly subjective question, one that has prompted powerfully impassioned discussions, and I will not attempt to argue one way or the other here.</p><p>That said, I think it's pretty cool.</p><p>Finally, I must give credit where credit is due. Thanks to <a href="http://andmkent.com">Andrew M. Kent</a> for the creation of the <a href="https://github.com/andmkent/datatype">datatype</a> package, which served as the inspiration for this blog post. Many thanks to <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> for his work creating Typed Racket, as well as helping me dramatically simplify the implementation used in this blog post. Also thanks to <a href="http://www.ccs.neu.edu/home/ryanc/">Ryan Culpepper</a> and <a href="http://www.ccs.neu.edu/home/matthias/">Matthias Felleisen</a> for their work on creating <code>syntax/parse</code>, which is truly a marvelous tool for exploring the world of macros, and, of course, a big thanks to <a href="http://www.cs.utah.edu/~mflatt/">Matthew Flatt</a> for his implementation of hygiene in Racket, as well as much of the rest of Racket itself. Not to mention the entire legacy of those who formulated the foundations of the Scheme macro system and created the framework for all of this to be possible so many decades later.</p><p>Truly, working in Racket feels like standing on the shoulders of giants. If you're intrigued, give it a shot. It's a fun feeling.</p><ol class="footnotes"></ol></article> \ No newline at end of file diff --git a/feeds/types.atom.xml b/feeds/types.atom.xml new file mode 100644 index 0000000..8a820f8 --- /dev/null +++ b/feeds/types.atom.xml @@ -0,0 +1,558 @@ +Posts tagged ‘types’ | Alexis King’s Blog2021-03-25T00:00:00ZAn introduction to typeclass metaprogramming2021-03-25T00:00:00Z2021-03-25T00:00:00ZAlexis King<article><p><em>Typeclass metaprogramming</em> is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem), and it is the core mechanism used to implement generic programming via <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">GHC generics</a>. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.</p><p>This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does <em>not</em> attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also <em>not</em> a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.</p><h2><a name="part-1-basic-building-blocks"></a>Part 1: Basic building blocks</h2><p>Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.</p><p>To start, we’ll cover the absolute foundations of typeclass metaprogramming.</p><h3><a name="typeclasses-as-functions-from-types-to-terms"></a>Typeclasses as functions from types to terms</h3><p>As its name implies, typeclass metaprogramming (henceforth TMP<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup>) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic <code>==</code> operator via the <code>Eq</code> class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: <strong>typeclasses are functions from types to (runtime) terms</strong>.</p><p>What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called <code>TypeOf</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Given these instances, we can observe that they do what we expect in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>Note that both the <code>TypeOf Bool</code> and <code>TypeOf Char</code> instances ignore the argument to <code>typeOf</code> altogether. This makes sense, as the whole point of the <code>TypeOf</code> class is to get access to <em>type</em> information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This typeclass definition is a little unusual, as the type parameter <code>a</code> doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>the full type of the <code>show</code> method is implicitly extended with a <code>Show a</code> constraint to yield:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Furthermore, if we write <code>forall</code>s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the <em>full</em> type of <code>show</code>:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>In the same vein, we can write out the full type of <code>typeOf</code>, as given by our new definition of <code>TypeOf</code>:</p><pre><code class="pygments"><span class="nf">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This type is still unusual, as the <code>a</code> type parameter doesn’t appear anywhere to the right of the <code>=&gt;</code> arrow. This makes the type parameter trivially <em>ambiguous</em>, which is to say it’s impossible for GHC to infer what <code>a</code> should be at any call site. Fortunately, <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html">we can use <code>TypeApplications</code></a> to pass a type for <code>a</code> directly, as we can see in the updated definition of <code>TypeOf (a, b)</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Once again, we can test out our new definitions in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="kt">Bool</span> +<span class="s">"Bool"</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Bool</span><span class="p">,</span><span class="w"> </span><span class="kt">Char</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our <code>typeOf</code> function is, quite literally, a function that accepts a single type as an argument and returns a term-level <code>String</code>. Of course, the <code>TypeOf</code> typeclass is not a particularly <em>useful</em> example of such a function, but it demonstrates how easy it is to construct.</p><h3><a name="type-level-interpreters"></a>Type-level interpreters</h3><p>One important consequence of eliminating the value-level argument of <code>typeOf</code> is that there is no need for its argument type to actually be <em>inhabited</em>. For example, consider the <code>TypeOf</code> instance on <code>Void</code> from <code>Data.Void</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Void</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Void"</span></code></pre><p>This above instance is no different from the ones on <code>Bool</code> and <code>Char</code> even though <code>Void</code> is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of <code>TypeOf</code> on lists:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"["</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"]"</span></code></pre><p>If <code>typeOf</code> required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type <code>a</code> to recursively apply <code>typeOf</code> to. But since <code>typeOf</code> only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.</p><p>A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Z</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="n">a</span></code></pre><p>It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:</p><ul><li><p><code>Z</code> is a type that represents 0.</p></li><li><p><code>S Z</code> is a type that represents 1.</p></li><li><p><code>S (S Z)</code> is a type that represents 2.</p></li></ul><p>And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Numeric.Natural</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>As its name implies, <code>reifyNat</code> reifies a type-level natural number encoded using our datatypes above into a term-level <code>Natural</code> value:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="kt">Z</span> +<span class="mi">0</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span> +<span class="mi">1</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="mi">2</span></code></pre><p>One way to think about <code>reifyNat</code> is as an <em>interpreter</em> of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.</p><h3><a name="overlapping-instances"></a>Overlapping instances</h3><p>Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for <code>Show (Maybe a)</code>, you aren’t supposed to <em>also</em> write an instance for <code>Show (Maybe Bool)</code>, since it isn’t clear whether <code>show (Just True)</code> should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.</p><p>Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.</p><p>As a simple example, suppose we wanted to write a typeclass that checks whether a given type is <code>()</code> or not:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:</p><pre><code class="pygments"><span class="c1">-- not actually valid Haskell, just an example</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>But if we try to translate this to typeclass instances, we’ll get a problem:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> This means GHC will complain about instance overlap if we try to evaluate <code>isUnit @()</code>:</p><pre><code>ghci&gt; isUnit @() + +error: + • Overlapping instances for IsUnit () + arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance IsUnit () +</code></pre><p>To fix this, we have to explicitly mark <code>IsUnit ()</code> as overlapping:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>Now GHC accepts the expression without complaint:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="nb">()</span> +<span class="kt">True</span></code></pre><p>What does the <code>{-# OVERLAPPING #-}</code> pragma do, exactly? The gory details are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances">spelled out in the GHC User’s Guide</a>, but the simple explanation is that <code>{-# OVERLAPPING #-}</code> relaxes the overlap checker as long as the instance is <em>strictly more specific</em> than the instance(s) it overlaps with. In this case, that is true: <code>IsUnit ()</code> is trivially more specific than <code>IsUnit a</code>, since the former only matches <code>()</code> while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.</p><p>Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>The intent of <code>guardUnit</code> is to use <code>isUnit</code> to detect if its argument is of type <code>()</code>, and if it is, to return an error. However, even though we marked <code>IsUnit ()</code> overlapping, we still get an overlapping instance error:</p><pre><code>error: + • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance [overlapping] IsUnit () + • In the expression: isUnit @a +</code></pre><p>What gives? The problem is that GHC simply doesn’t know what type <code>a</code> is when compiling <code>guardUnit</code>. It <em>could</em> be instantiated to <code>()</code> where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.</p><p>This behavior is actually a very, very good thing. If GHC were to blindly pick the <code>IsUnit a</code> instance in this case, then <code>guardUnit</code> would always take the <code>False</code> branch, even when passed a value of type <code>()</code>! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when <code>{-# OVERLAPPING #-}</code> annotations are used, so it’s important to keep their limitations in mind.</p><p>As it happens, in this particular case, the error is easily remedied. We simply have to add an <code>IsUnit</code> constraint to the type signature of <code>guardUnit</code>:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now picking the right <code>IsUnit</code> instance is deferred to the place where <code>guardUnit</code> is used, and the definition is accepted.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><h3><a name="type-families-are-functions-from-types-to-types"></a>Type families are functions from types to types</h3><p>In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE TypeFamilies #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>The above is a <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families">closed type family</a>, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of <code>Sum</code> would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="nf">sum</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span> +<span class="nf">sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="nf">sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="n">sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.</p><p>To test our definition of <code>Sum</code> in GHCi, we can use <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind">the <code>:kind!</code> command</a>, which prints out a type and its kind after reducing it as much as possible:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span></code></pre><p>We can also combine <code>Sum</code> with our <code>ReifyNat</code> class from earlier:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.</p><h3><a name="example-1-generalized-concat"></a>Example 1: Generalized <code>concat</code></h3><p>Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a <code>flatten</code> function similar to like-named functions provided by many dynamically-typed languages. In those languages, <code>flatten</code> is like <code>concat</code>, but it works on a list of arbitrary depth. For example, we might use it like this:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]</span></code></pre><p>In Haskell, lists of different depths have different types, so multiple levels of <code>concat</code> have to be applied explicitly. But using TMP, we can write a generic <code>flatten</code> function that operates on lists of any depth!</p><p>Since this is <em>typeclass</em> metaprogramming, we’ll unsurprisingly begin with a typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="o">???</span><span class="p">]</span></code></pre><p>Our first challenge is writing the return type of <code>flatten</code>. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>Now we can write our <code>Flatten</code> instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>The inductive case is when the type is a nested list, in which case we want to apply <code>concat</code> and recur:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">(</span><span class="n">concat</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Sadly, if we try to compile these definitions, GHC will reject our <code>Flatten [a]</code> instance:</p><pre><code>error: + • Couldn't match type ‘a’ with ‘ElementOf [a]’ + ‘a’ is a rigid type variable bound by + the instance declaration + Expected type: [ElementOf [a]] + Actual type: [a] + • In the expression: x + In an equation for ‘flatten’: flatten x = x + In the instance declaration for ‘Flatten [a]’ + | + | flatten x = x + | ^ +</code></pre><p>At first blush, this error looks very confusing. Why doesn’t GHC think <code>a</code> and <code>ElementOf [a]</code> are the same type? Well, consider what would happen if we picked a type like <code>[Int]</code> for <code>a</code>. Then <code>[a]</code> would be <code>[[Int]]</code>, a nested list, so the first case of <code>ElementOf</code> would apply. Therefore, GHC refuses to pick the second equation of <code>ElementOf</code> so hastily.</p><p>In this particular case, we might think that’s rather silly. After all, if <code>a</code> were <code>[Int]</code>, then GHC wouldn’t have picked the <code>Flatten [a]</code> instance to begin with, it would pick the more specific <code>Flatten [[a]]</code> instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.</p><p>Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our <code>Flatten [a]</code> instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>This is a <em>type equality constraint</em>. Type equality constraints are written with the syntax <code>a ~ b</code>, and they state that <code>a</code> must be the same type as <code>b</code>. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that <code>ElementOf [a]</code> must always be <code>a</code>, which allows the instance to typecheck.</p><p>Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must <em>eventually</em> be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the <code>Flatten [a]</code> instance is selected, GHC will know that <code>a</code> is <em>not</em> a list type, and it will be able to reduce <code>ElementOf [a]</code> to <code>a</code> without difficulty. Indeed, we can see this for ourselves by using <code>flatten</code> in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span></code></pre><p>It works! But why do we need the type annotation on <code>1</code>? If we leave it out, we get a rather hairy type error:</p><pre><code>error: + • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ + Expected type: [ElementOf [a]] + Actual type: [ElementOf [a0]] + NB: ‘ElementOf’ is a non-injective type family + The type variable ‘a0’ is ambiguous +</code></pre><p>The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a <code>Num [a]</code> instance, in which case <code>1</code> could actually have a list type, and either case of <code>ElementOf</code> could match depending on the choice of <code>Num</code> instance. Of course, no such <code>Num</code> instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.</p><p>This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call <code>flatten</code> on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.</p><p>That wrinkle aside, <code>flatten</code> is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.</p><h4><a name="typeclasses-as-compile-time-code-generation"></a>Typeclasses as compile-time code generation</h4><p>Presented with the above definition of <code>Flatten</code>, it might not be immediately obvious how to think about <code>Flatten</code> as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, <code>Eq</code> or <code>Show</code>) than the <code>TypeOf</code> and <code>ReifyNat</code> classes we defined above.</p><p>One useful way to shift our perspective is to consider equivalent <code>Flatten</code> instances written using point-free style:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">id</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">concat</span></code></pre><p>These definitions of <code>flatten</code> no longer (syntactically) depend on term-level arguments, just like our definitions of <code>typeOf</code> and <code>reifyNat</code> didn’t accept any term-level arguments above. This allows us to consider what <code>flatten</code> might “expand to” given a type argument alone:</p><ul><li><p><code>flatten @[Int]</code> is just <code>id</code>, since the <code>Flatten [a]</code> instance is selected.</p></li><li><p><code>flatten @[[Int]]</code> is <code>flatten @[Int] . concat</code>, since the <code>Flatten [[a]]</code> instance is selected. That then becomes <code>id . concat</code>, which can be further simplified to just <code>concat</code>.</p></li><li><p><code>flatten @[[[Int]]]</code> is <code>flatten @[[Int]] . concat</code>, which simplifies to <code>concat . concat</code> by the same reasoning above.</p></li><li><p><code>flatten @[[[[Int]]]]</code> is then <code>concat . concat . concat</code>, and so on.</p></li></ul><p>This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of <code>flatten</code> takes a type as an argument and produces some number of composed <code>concat</code>s as a result. From this perspective, <code>Flatten</code> is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.</p><p>This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name <em>metaprogramming</em>. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.</p><h2><a name="part-2-generic-programming"></a>Part 2: Generic programming</h2><p>Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.</p><p>In the previous section, we discussed how to use TMP to write a generic <code>flatten</code> operation. In this section, we’ll aim a bit higher: totally generic functions that operate on <em>arbitrary</em> datatypes.</p><h3><a name="open-type-families-and-associated-types"></a>Open type families and associated types</h3><p>Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, <em>open type families</em>. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using <code>type instance</code> declarations. For example, we could define our <code>Sum</code> family from above like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>In the case of <code>Sum</code>, this would not be very useful, and indeed, <code>Sum</code> is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.</p><p>This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a <code>Key</code> open type family that relates types to the types used to index them:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span></code></pre><p>This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>In this case, anyone could define their own data structure, define instances of <code>Key</code> and <code>HasKey</code> for their data structure, and use <code>hasKey</code> to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>An open family declared inside a typeclass like this is called an <em>associated type</em>. It works exactly the same way as the separate definitions of <code>Key</code> and <code>HasKey</code>, it just uses a different syntax. Note that although the <code>family</code> and <code>instance</code> keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).</p><p>Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like <a href="https://hackage.haskell.org/package/mono-traversable"><code>mono-traversable</code></a> are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.</p><p>However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes <em>heavy</em> use of open type families: datatype-generic programming.</p><h3><a name="example-2-datatype-generic-programming"></a>Example 2: Datatype-generic programming</h3><p><em>Datatype-generic programming</em> refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include</p><ul><li><p>equality, comparison, and hashing,</p></li><li><p>recursive traversal of self-similar data structures, and</p></li><li><p>serialization and deserialization,</p></li></ul><p>among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on <em>any</em> datatype.</p><p>In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.</p><h4><a name="generic-datatype-representations"></a>Generic datatype representations</h4><p>At the heart of the <code>Generic</code> class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Authentication</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AuthBasic</span><span class="w"> </span><span class="kt">Username</span><span class="w"> </span><span class="kt">Password</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AuthSSH</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>then we have a type that is essentially equivalent to this one:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>If we know how to define a function on a nested tree built out of <code>Either</code>s and pairs, then we know how to define it on <em>any</em> such datatype! This is where TMP comes in: recall the way we viewed <code>Flatten</code> as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?</p><p>The answer to that question is <em>yes</em>. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, <code>numFields (AuthBasic "alyssa" "pass1234")</code> would return <code>2</code>, while <code>numFields (AuthSSH "&lt;key&gt;")</code> would return <code>1</code>. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.</p><p>We’ll start by using TMP to implement a “generic” version of <code>numFields</code> that operates on trees of <code>Either</code>s and pairs as described above:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="c1">-- base case: leaf value</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>Just like our <code>Flatten</code> class from earlier, <code>GNumFields</code> uses the type-level structure of its argument to choose what to do:</p><ul><li><p>If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.</p></li><li><p>If we find <code>Left</code> or <code>Right</code>, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.</p></li><li><p>In the case of any other value, we’re at a “leaf” in the tree of <code>Either</code>s and pairs, which corresponds to a single field, so we just return <code>1</code>.</p></li></ul><p>Now if we call <code>gnumFields (Left ("alyssa", "pass1234"))</code>, we’ll get <code>2</code>, and if we call <code>gnumFields (Right "&lt;key&gt;")</code>, we’ll get <code>1</code>. All that’s left to do is write a bit of code that converts our <code>Authentication</code> type to a tree of <code>Either</code>s and pairs:</p><pre><code class="pygments"><span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericizeAuthentication</span></code></pre><p>Now we get the results we want on our <code>Authentication</code> type using <code>numFieldsAuthentication</code>, but we’re not done yet, since it only works on <code>Authentication</code> values. Is there a way to define a generic <code>numFields</code> function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFields</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericize</span></code></pre><p>Now <code>numFields (AuthBasic "alyssa" "pass1234")</code> returns <code>2</code>, as desired, and it will <em>also</em> work with any datatype that provides a <code>Generic</code> instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:</p><ul><li><p>First, we define the <code>Generic</code> class, comprised of two parts:</p><ol><li><p>The <code>Rep a</code> associated type maps a type <code>a</code> onto its generic, sums-of-products representation, i.e. one built out of combinations of <code>Either</code> and pairs.</p></li><li><p>The <code>genericize</code> method converts an actual <em>value</em> of type <code>a</code> to the equivalent value using the sums-of-products representation.</p></li></ol></li><li><p>Next, we define a <code>Generic</code> instance for <code>Authentication</code>. <code>Rep Authentication</code> is the sums-of-products representation we described above, and <code>genericize</code> is likewise <code>genericizeAuthentication</code> from above.</p></li><li><p>Finally, we define <code>numFields</code> as a function with a <code>GNumFields (Rep a)</code> constraint. This is where all the magic happens:</p><ul><li><p>When we apply <code>numFields</code> to a datatype, <code>Rep</code> retrieves its generic, sums-of-products representation type.</p></li><li><p>The <code>GNumFields</code> class then uses various TMP techniques we’ve already described so far in this blog post to generate a <code>numFields</code> implementation on the fly from the structure of <code>Rep a</code>.</p></li><li><p>Finally, that generated <code>numFields</code> implementation is applied to the genericized term-level value, and the result is produced.</p></li></ul></li></ul><p>After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) <code>numFields</code> operation. Surely just defining <code>numFields</code> on each type directly would be far easier? Indeed, if we were just considering <code>numFields</code>, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined <code>numFields</code>, and all of them would automatically work on <code>Authentication</code> because they all leverage the same <code>Generic</code> instance!</p><p>This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation <em>once</em>, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.</p><h4><a name="improving-our-definition-of-generic"></a>Improving our definition of <code>Generic</code></h4><p>You may note that the definition of <code>Generic</code> provided above does not match the one in <code>GHC.Generic</code>. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a <code>GHC.Generics</code> tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.</p><h5><a name="distinguishing-leaves-from-the-spine"></a>Distinguishing leaves from the spine</h5><p>One problem with our version of <code>Generic</code> is that it provides no way to distinguish an <code>Either</code> or pair that should be considered a “leaf”, as in a type like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">A</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">B</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">)</span></code></pre><p>Given this type, <code>Rep Foo</code> should be <code>Either (Either Int String) (Char, Bool)</code>, and <code>numFields (Right ('a', True))</code> will erroneously return <code>2</code> rather than <code>1</code>. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">getLeaf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Now our <code>Generic</code> instances look like this:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">PublicKey</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">key</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">))</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">A</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">B</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Since the <code>Leaf</code> constructor now distinguishes a leaf, rather than the absence of an <code>Either</code> or <code>(,)</code> constructor, we’ll have to update our <code>GNumFields</code> instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.</p><h5><a name="handling-empty-constructors"></a>Handling empty constructors</h5><p>Suppose we have a type with nullary data constructors, like the standard <code>Bool</code> type:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>How do we write a <code>Generic</code> instance for <code>Bool</code>? Using just <code>Either</code>, <code>(,)</code>, and <code>Leaf</code>, we can’t, but if we are willing to add a case for <code>()</code>, we can use it to denote nullary constructors:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="nb">()</span></code></pre><p>In a similar vein, we could use <code>Void</code> to represent datatypes that don’t have any constructors at all.</p><h4><a name="continuing-from-here"></a>Continuing from here</h4><p>The full version of <code>Generic</code> has a variety of further improvements useful for generic programming, including:</p><ul><li><p>Support for converting from <code>Rep a</code> to <code>a</code>.</p></li><li><p>Special indication of self-recursive datatypes, making generic tree traversals possible.</p></li><li><p>Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.</p></li><li><p>Fully automatic generation of <code>Generic</code> instances via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric">the <code>DeriveGeneric</code> extension</a>, which reduces the per-type boilerplate to essentially nothing.</p></li></ul><p>The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.</p><h2><a name="part-3-dependent-typing"></a>Part 3: Dependent typing</h2><p>It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.</p><h3><a name="datatype-promotion"></a>Datatype promotion</h3><p>In part 1, we used uninhabited datatypes like <code>Z</code> and <code>S a</code> to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, <code>True</code> and <code>False</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">True</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">False</span></code></pre><p>Now we could define type families to provide operations on these types, such as <code>Not</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>However, this has some frustrating downsides:</p><ul><li><p>First, it’s simply inconvenient that we have to define these new <code>True</code> and <code>False</code> “dummy” types, which are completely distinct from the <code>Bool</code> type provided by the prelude.</p></li><li><p>More significantly, it means <code>Not</code> has a very unhelpful kind:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span></code></pre><p>Even though <code>Not</code> is only <em>supposed</em> to be applied to <code>True</code> or <code>False</code>, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like <code>Not Char</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span> +<span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span></code></pre><p>Rather than getting an error, GHC simply spits <code>Not Char</code> back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.</p></li></ul><p>One way to think about <code>Not</code> is that it is largely <em>dynamically kinded</em> in the same way some languages are dynamically typed. That isn’t entirely true, as we technically <em>will</em> get a kind error if we try to apply <code>Not</code> to a type constructor rather than a type, such as <code>Maybe</code>:</p><pre><code>ghci&gt; :kind! Not Maybe + +&lt;interactive&gt;:1:5: error: + • Expecting one more argument to ‘Maybe’ + Expected a type, but ‘Maybe’ has kind ‘* -&gt; *’ +</code></pre><p>…but <code>*</code> is still a very big kind, much bigger than we would like to permit for <code>Not</code>.</p><p>To help with both these problems, GHC provides <em>datatype promotion</em> via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html">the <code>DataKinds</code> language extension</a>. The idea is that for each normal, non-GADT type definition like</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>then in addition to the normal type constructor and value constructors, GHC also defines several <em>promoted</em> constructors:</p><ul><li><p><code>Bool</code> is allowed as both a type and a kind.</p></li><li><p><code>'True</code> and <code>'False</code> are defined as new types of kind <code>Bool</code>.</p></li></ul><p>We can see this in action if we remove our <code>data True</code> and <code>data False</code> declarations and adjust our definition of <code>Not</code> to use promoted constructors:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DataKinds #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;True</span></code></pre><p>Now the inferred kind of <code>Not</code> is no longer <code>* -&gt; *</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>Consequently, we will now get a kind error if we attempt to apply <code>Not</code> to anything other than <code>'True</code> or <code>'False</code>:</p><pre><code>ghci&gt; :kind! Not Char + +&lt;interactive&gt;:1:5: error: + • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ +</code></pre><p>This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">&#39;Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">&#39;S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>Note that we need to add an explicit kind signature on the definition of the <code>ReifyNat</code> typeclass, since otherwise GHC will assume <code>a</code> has kind <code>*</code>, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that <code>Z</code> and <code>S</code> are related, this prevents someone from coming along and defining a nonsensical instance like <code>ReifyNat Char</code>, which previously would have been allowed but will now be rejected with a kind error.</p><p>Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.</p><h3><a name="gadts-and-proof-terms"></a>GADTs and proof terms</h3><p>So far in this blog post, we have discussed several different function-like things:</p><ul><li><p>Ordinary Haskell functions are functions from terms to terms.</p></li><li><p>Type families are functions from types to types.</p></li><li><p>Typeclasses are functions from types to terms.</p></li></ul><p>A curious reader may wonder about the existence of a fourth class of function:</p><ul><li><p><em>???</em> are functions from terms to types.</p></li></ul><p>To reason about what could go in the <em>???</em> above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?</p><p>In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.</p><p>GADTs<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup> are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html">described in detail in the GHC User’s Guide</a>, but the key idea for our purposes is that <em>pattern-matching on a GADT constructor can refine type information</em>. Here’s a simple, silly example:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Bool</span> +<span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Int</span> + +<span class="nf">doSomething</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">x</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span></code></pre><p>Here, <code>WhatIsIt</code> is a datatype with two nullary constructors, <code>ABool</code> and <code>AnInt</code>, similar to a normal, non-GADT datatype like this one:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AnInt</span></code></pre><p>What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, <code>ABool</code> and <code>AnInt</code> would both have the type <code>forall a. WhatIsIt a</code>, but in the GADT definition, we explicitly fix <code>a</code> to <code>Bool</code> in the type of <code>ABool</code> and to <code>Int</code> in the type of <code>AnInt</code>.</p><p>This simple feature allows us to do very interesting things. The <code>doSomething</code> function is polymorphic in <code>a</code>, but on the right-hand side of the first equation, <code>x</code> has type <code>Bool</code>, while on the right-hand side of the second equation, <code>x</code> has type <code>Int</code>. This is because the <code>WhatIsIt a</code> argument effectively constrains the type of <code>a</code>, as we can see by experimenting with <code>doSomething</code> in GHCi:</p><pre><code>ghci&gt; doSomething ABool True +False +ghci&gt; doSomething AnInt 10 +11 +ghci&gt; doSomething AnInt True + +error: + • Couldn't match expected type ‘Int’ with actual type ‘Bool’ + • In the second argument of ‘doSomething’, namely ‘True’ + In the expression: doSomething AnInt True + In an equation for ‘it’: it = doSomething AnInt True +</code></pre><p>One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The <code>ABool</code> constructor is a proof of <code>a ~ Bool</code>, while the <code>AnInt</code> constructor is a proof of <code>a ~ Int</code>. When you construct <code>ABool</code> or <code>AnInt</code>, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.</p><p>GADTs can be much more sophisticated than our simple <code>WhatIsIt</code> type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:</p><pre><code class="pygments"><span class="kr">infixr</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span> + +<span class="kr">data</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">HCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This type is a <em>heterogenous list</em>, a list that can contain elements of different types:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">t</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Num</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[Bool, [Char]</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>An <code>HList</code> is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total <code>head</code> function on <code>HList</code>s like this:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Remarkably, GHC does not complain that this definition of <code>head</code> is non-exhaustive. Since we specified that the argument must be of type <code>HList (a ': as)</code> in the type signature for <code>head</code>, GHC knows that the argument <em>cannot</em> be <code>HNil</code> (which would have the type <code>HList '[]</code>), so it doesn’t ask us to handle that case.</p><p>These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.</p><h4><a name="proofs-that-work-together"></a>Proofs that work together</h4><p>This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an <code>HList</code> of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with <code>head</code>, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.</p><p>However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a <em>proof term</em> that contains no values, it just encapsulates type equalities on a type-level list:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a]</span> +<span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b]</span> +<span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b, c]</span></code></pre><p>We call it a proof term because a value of type <code>OneToThree a b c as</code> constitutes a <em>proof</em> that <code>as</code> has exactly 1, 2, or 3 elements. Using <code>OneToThree</code>, we can write a function that accepts an <code>HList</code> accompanied by a proof term:</p><pre><code class="pygments"><span class="nf">sumUpToThree</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">z</span></code></pre><p>As with <code>head</code>, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:</p><ul><li><p>When we match on the <code>OneToThree</code> proof term, information flows from the term level to the type level, refining the type of <code>as</code> in that branch.</p></li><li><p>The refined type of <code>as</code> then flows back down to the term level, restricting the shape the <code>HList</code> can take and refinine the set of patterns we have to match.</p></li></ul><p>Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an <code>HList</code> has an even number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This is a proof which itself has inductive structure: <code>EvenCons</code> takes a proof that <code>as</code> has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an <code>HList</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span> + +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Once again, this definition is completely exhaustive, and we can show that it works in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="kt">EvenNil</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.</p><h4><a name="proof-inference"></a>Proof inference</h4><p>While our definition of <code>pairUp</code> is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the <code>Even</code> proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.</p><p>Remember that typeclasses are functions from types to terms. As its happens, a value of type <code>Even as</code> can be mechanically produced from the structure of the type <code>as</code>. This suggests that we could use TMP to automatically generate <code>Even</code> proofs, and indeed, we can. In fact, it’s not at all complicated:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>We can now adjust our <code>pairUp</code> function to use <code>IsEven</code> instead of an explicit <code>Even</code> argument:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>This is essentially identical to its old definition, but by acquiring the proof via <code>IsEven</code> rather than passing it explicitly, we can call <code>pairUp</code> without having to construct a proof manually:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This is rather remarkable. Using TMP, we are able to get GHC to <em>automatically construct a proof that a list is even</em>, with no programmer guidance beyond writing the <code>IsEven</code> typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: <code>IsEven</code> is a function that accepts a type-level list and generates an <code>Even</code> proof term.</p><p>From this perspective, <strong>typeclasses are a way of specifying a proof search algorithm</strong> to the compiler. In the case of <code>IsEven</code>, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.</p><h3><a name="aside-gadts-versus-type-families"></a>Aside: GADTs versus type families</h3><p>Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.</p><p>Consider again the type of the <code>pairUp</code> function above (without the typeclass for simplicity):</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>We used both a GADT, <code>Even</code>, and a type family, <code>PairUp</code>. But we could have, in theory, used <em>only</em> a GADT and eliminated the type family altogether. Consider this variation on the <code>Even</code> proof term:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="p">((</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span></code></pre><p>This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of <code>as</code> as an “input” parameter and <code>bs</code> as an “output” parameter. The idea is that any <code>EvenPairs</code> proof relates both an even-length list type and its paired up equivalent:</p><ul><li><p><code>EvenNil</code> has type <code>EvenPairs '[] '[]</code>,</p></li><li><p><code>EvenCons EvenNil</code> has type <code>EvenPairs '[a, b] '[(a, b)]</code>,</p></li><li><p><code>EvenCons (EvenCons EvenNil)</code> has type <code>EvenPairs '[a, b, c, d] '[(a, b), (c, d)]</code>,</p></li><li><p>…and so on.</p></li></ul><p>This allows us to reformulate our <code>pairUp</code> type signature this way:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">bs</span></code></pre><p>The definition is otherwise unchanged. The <code>PairUp</code> type family is completely gone, because now <code>EvenPairs</code> itself defines the relation. In this way, GADTs can be used like type-level functions!</p><p>The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Kind</span><span class="w"> </span><span class="p">(</span><span class="kt">Constraint</span><span class="p">)</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span></code></pre><p>The idea here is that <code>IsEvenTF as</code> produces a constraint can only be satisfied if <code>as</code> has an even number of elements, since that’s the only way it will eventually reduce to <code>()</code>, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting <code>IsEvenTF as =&gt;</code> in a type signature successfully restricts <code>as</code> to be an even-length list, but it doesn’t allow us to write <code>pairUp</code>. To see why, we can try the following definition:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Unlike the version using the GADT, this version of <code>pairUp</code> is not considered exhaustive:</p><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil +</code></pre><p>This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by <code>IsEvenTF</code> provides no term-level evidence about the shape of <code>as</code>, so we can’t branch on it the way we can branch on the <code>Even</code> GADT.<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> (In a sense, <code>IsEvenTF</code> is doing <a href="/blog/2019/11/05/parse-don-t-validate/">validation, not parsing</a>.)</p><p>For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.</p><h3><a name="guiding-type-inference"></a>Guiding type inference</h3><p>So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.</p><p>However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.</p><p>To illustrate what that can look like, suppose we want to use TMP to generate an <code>HList</code> full of <code>()</code> values of an arbitrary length:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Testing in GHCi, we can see it behaves as desired:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[(), (), ()]</span> +<span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>Now suppose we write a function that accepts a list containing exactly one element and returns it:</p><pre><code class="pygments"><span class="nf">unsingleton</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[a]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unsingleton</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Naturally, we would expect these to compose without a hitch. If we write <code>unsingleton unitList</code>, our TMP should generate a list of length 1, and we should get back <code>()</code>. However, it may surprise you to learn that <em>isn’t</em>, in fact, what happens:<sup><a href="#footnote-6" id="footnote-ref-6-1">6</a></sup></p><pre><code>ghci&gt; unsingleton unitList + +error: + • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ + prevents the constraint ‘(UnitList '[a0])’ from being solved. + Probable fix: use a type annotation to specify what ‘a0’ should be. + These potential instances exist: + instance UnitList as =&gt; UnitList (() : as) +</code></pre><p>What went wrong? The type error says that <code>a0</code> is ambiguous, but it only lists a single matching <code>UnitList</code> instance—the one we want—so how can it be ambiguous which one to select?</p><p>The problem stems from the way we defined <code>UnitList</code>. When we wrote the instance</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span></code></pre><p>we said the first element of the type-level list must be <code>()</code>, so there’s nothing stopping someone from coming along and defining another instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="kt">Int</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>In that case, GHC would have no way to know which instance to pick. Nothing in the type of <code>unsingleton</code> forces the element in the list to have type <code>()</code>, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.</p><p>Of course, this isn’t what we want. The <code>UnitList</code> class is supposed to <em>always</em> return a list of <code>()</code> values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Here we’ve changed the instance so that it has the shape <code>UnitList (a ': as)</code>, with a type variable in place of the <code>()</code>, but we also added an equality constraint that forces <code>a</code> to be <code>()</code>. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unsingleton</span><span class="w"> </span><span class="n">unitList</span> +<span class="nb">()</span></code></pre><p>To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="o">&lt;</span><span class="n">constraints</span><span class="o">&gt;</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">C</span><span class="w"> </span><span class="o">&lt;</span><span class="n">types</span><span class="o">&gt;</span></code></pre><p>The part to the left of the <code>=&gt;</code> is known as the <em>instance context</em>, while the part to the right is known as the <em>instance head</em>. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, <strong>only the instance head matters, and the instance context is completely ignored</strong>. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.</p><p>This explains why our two <code>UnitList</code> instances behave differently:</p><ul><li><p>Given the instance head <code>UnitList (() ': as)</code>, GHC won’t select the instance unless it knows the first element of the list is <code>()</code>.</p></li><li><p>But given the instance head <code>UnitList (a ': as)</code>, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.</p></li></ul><p>After the <code>UnitList (a ': as)</code> instance is selected, GHC attempts to solve the constraints in the instance context, including the <code>a ~ ()</code> constraint. This <em>forces</em> <code>a</code> to be <code>()</code>, resolving the ambiguity and allowing type inference to proceed.</p><p>This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:</p><ul><li><p>If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.</p></li><li><p>But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell <em>you</em> what type this is,” effectively giving you a role in type inference itself.</p></li></ul><p>From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.</p><p>Given all of the above, consider again the definition of <code>IsEven</code> from earlier:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an <code>IsEven (a ': b0)</code> constraint, where <code>b0</code> is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an <code>IsEven '[a]</code> instance in the future.</p><p>To fix this, we can apply the same trick we used for <code>UnitList</code>, just in a slightly different way:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Again, the idea is to move the type information we <em>learn</em> from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can <strong>dramatically</strong> improve type inference in programs that make heavy use of TMP.</p><h3><a name="example-3-subtyping-constraints"></a>Example 3: Subtyping constraints</h3><p>At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at <a href="https://hasura.io/">Hasura</a>, I had the opportunity to design an internal parser combinator library that captures aspects of the <a href="https://graphql.org/">GraphQL</a> type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.</p><p>Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our <code>GQLKind</code> datatype has three cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLKind</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Both</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Input</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Output</span></code></pre><p>We use <code>DataKind</code>-promoted versions of this <code>GQLKind</code> type as a parameter to a <code>GQLType</code> GADT:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">TScalar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Both</span> +<span class="w"> </span><span class="kt">TInputObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">InputObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Input</span> +<span class="w"> </span><span class="kt">TIObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Output</span> +<span class="w"> </span><span class="c1">-- ...and so on...</span></code></pre><p>This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type <code>GQLType 'Input</code>, we can’t pass a <code>GQLType 'Both</code>, even though we really ought to be able to.</p><p>To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span></code></pre><p>The first case, <code>KRefl</code>, states that every kind is trivially a subkind of itself. The second case, <code>KBoth</code>, states that <code>Both</code> is a subkind of any kind at all. (This is a particularly literal example of <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">using a type to define axioms</a>.) The next step is to use TMP to implement proof inference:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KBoth</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span></code></pre><p>These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that <code>k</code> is a superkind of <code>'Input</code> or <code>'Output</code>, type inference will force them to be equal.</p><p>Using <code>IsSubKind</code>, we can easily resolve the problem described above. Rather than write a function with a type like this:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>…we simply use an <code>IsSubKind</code> constraint, instead:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now both <code>'Input</code> and <code>'Both</code> kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. <em>Consuming</em> the <code>SubKind</code> proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SelectionSet</span></code></pre><p>This type family is used to determine what a <code>GQLParser k a</code> actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>.</p><p>Fortunately, that is very easy to do using <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html">the <code>(:~:)</code> type from <code>Data.Type.Equality</code> in <code>base</code></a> to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">a</span></code></pre><p>Just as with any other GADT, <code>(:~:)</code> can be used to pack up type equalities and unpack them later; <code>a :~: b</code> just happens to be the GADT that corresponds precisely to the equality <code>a ~ b</code>. Using <code>(:~:)</code>, we can write a reusable proof that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>:</p><pre><code class="pygments"><span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="o">@</span><span class="kt">&#39;Input</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span></code></pre><p>This function is a very simple proof by cases, where <code>Refl</code> can be read as “Q.E.D.”:</p><ul><li><p>In the first case, matching on <code>KRefl</code> refines <code>k</code> to <code>'Input</code>, and <code>ParserInput 'Input</code> is <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li><li><p>Likewise, in the second case, matching on <code>KBoth</code> refines <code>k</code> to <code>'Both</code>, and <code>ParserInput 'Both</code> is also <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li></ul><p>This <code>inputParserInput</code> helper allows functions like <code>nullable</code>, which internally need <code>ParserInput k ~ InputValue</code>, to take the form</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nullable</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">inputParserInput</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ...implementation goes here... -}</span></code></pre><p>Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without <code>IsSubKind</code> doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!</p><h2><a name="wrapping-up-and-closing-thoughts"></a>Wrapping up and closing thoughts</h2><p>So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:</p><ul><li><p>Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.</p></li><li><p>Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.</p></li><li><p>Though I’ve called the technique “<em>typeclass</em> metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.</p></li><li><p>Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.</p></li></ul><p>The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.</p><p>Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.</p><p>It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:</p><ul><li><p>As mentioned earlier in this blog post, <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">the <code>GHC.Generics</code> module documentation</a> is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.</p></li><li><p>I have long believed that <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/">the GHC User’s Guide</a> is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.</p></li><li><p>Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the <a href="https://hackage.haskell.org/package/singletons">singletons</a> library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, <a href="https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf">the accompanying paper</a> is definitely worth a read if you’d like to go down that route.)</p></li></ul><p>Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.</p><ol class="footnotes"><li id="footnote-1"><p>Not to be confused with C++’s <a href="https://en.wikipedia.org/wiki/Template_metaprogramming"><em>template</em> metaprogramming</a>, though there are significant similarities between the two techniques. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>There have been proposals to introduce ordered instances, known in the literature as <a href="https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf"><em>instance chains</em></a>, but as of this writing, GHC does not implement them. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Note that this also preserves an important property of the Haskell type system, parametricity. A function like <code>id :: a -&gt; a</code> shouldn’t be allowed to do different things depending on which type is chosen for <code>a</code>, which our first version of <code>guardUnit</code> tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Short for <em>generalized algebraic datatypes</em>, which is a rather unhelpful name for actually understanding what they are or what they’re for. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for <code>IsEvenTF</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">exists</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">as&#39;</span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">)</span></code></pre><p>The type refinement provided by matching on <code>HCons</code> would be enough for the second case of <code>IsEvenTF</code> to be selected, which would provide an equality proof that <code>as</code> has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. <a href="#footnote-ref-5-1">↩</a></p></li><li id="footnote-6"><p>Actually, I’ve cheated a little bit here, because <code>unsingleton unitList</code> really does typecheck in GHCi under normal circumstances. That’s because <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules">the <code>ExtendedDefaultRules</code> extension</a> is enabled in GHCi by default, which defaults ambiguous type variables to <code>()</code>, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned <code>ExtendedDefaultRules</code> off to illustrate the problem. <a href="#footnote-ref-6-1">↩</a></p></li></ol></article>Names are not type safety2020-11-01T00:00:00Z2020-11-01T00:00:00ZAlexis King<article><p>Haskell programmers spend a lot of time talking about <em>type safety</em>. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published <a href="/blog/2019/11/05/parse-don-t-validate/">Parse, Don’t Validate</a> as an initial stab towards bridging that gap.</p><p>The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s <code>newtype</code> construct. The idea is simple enough—the <code>newtype</code> keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this <em>sounds</em> like a simple and straightforward path to type safety. For example, one might consider using a <code>newtype</code> declaration to define a type for an email address:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This technique can provide <em>some</em> value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct <em>kind</em> of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.</p><p>And names are not type safety.</p><h2><a name="intrinsic-and-extrinsic-safety"></a>Intrinsic and extrinsic safety</h2><p>To illustrate the difference between constructive data modeling (discussed at length in my <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">previous blog post</a>) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">One</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Two</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Three</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Four</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Five</span></code></pre><p>We could then write some functions to convert between <code>Int</code> and our <code>OneToFive</code> type:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">One</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Two</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Three</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Four</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Five</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">3</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">5</span></code></pre><p>This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="kt">Int</span></code></pre><p>Just as before, we can provide <code>toOneToFive</code> and <code>fromOneToFive</code> functions, with identical types:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">otherwise</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="p">(</span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">n</span></code></pre><p>If we put these declarations in their own module and choose not to export the <code>OneToFive</code> constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.</p><p>To see why, suppose we write a function that consumes a <code>OneToFive</code> value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"first"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"second"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"third"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fifth"</span></code></pre><p>The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an <code>Int</code>—after all, it <em>is</em> an <code>Int</code>. An <code>Int</code> can of course contain many other values besides <code>1</code> through <code>5</code>, so we are forced to add an error case to satisfy the exhaustiveness checker:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromOneToFive</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"first"</span> +<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"second"</span> +<span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"third"</span> +<span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fifth"</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: bad OneToFive value"</span></code></pre><p>In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:</p><ul><li><p>The constructive datatype captures its invariants in such a way that they are <em>accessible</em> to downstream consumers. This frees our <code>ordinal</code> function from worrying about handling illegal values, as they have been made unutterable.</p></li><li><p>The newtype wrapper provides a smart constructor that <em>validates</em> the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting <code>Int</code>s.</p></li></ul><p>Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of <code>error</code> has punched a hole right through our type system. If we were to add another constructor to our <code>OneToFive</code> datatype,<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> the version of <code>ordinal</code> that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.</p><p>All of this is a consequence of the fact that the constructive modeling is <em>intrinsically</em> type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent <code>6</code> using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an <code>Int</code>; its meaning is specified extrinsically via the <code>toOneToFive</code> smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.</p><h3><a name="revisiting-non-empty-lists"></a>Revisiting non-empty lists</h3><p>Our <code>OneToFive</code> datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the <code>NonEmpty</code> datatype I’ve repeatedly highlighted in recent blog posts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>It may be illustrative to imagine a version of <code>NonEmpty</code> represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Foldable</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Just as with <code>OneToFive</code>, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for <code>NonEmpty</code> was the ability to write a safe version of <code>head</code>, but the newtype version requires another assertion:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">x</span> +<span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines <code>NonEmpty</code>, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.</p><h2><a name="newtypes-as-tokens"></a>Newtypes as tokens</h2><p>If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes <em>can</em> provide a sort of safety, just a weaker one.</p><p>The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a <em>trust boundary</em> where internal invariants are enforced by restricting clients to a safe API.</p><p>We can use the <code>NonEmpty</code> example from above to illustrate how this works. We refrain from exporting the <code>NonEmpty</code> constructor, and we provide <code>head</code> and <code>tail</code> operations that we trust to never actually fail:</p><pre><code class="pygments"><span class="kr">module</span><span class="w"> </span><span class="nn">Data.List.NonEmpty.Newtype</span> +<span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">NonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">cons</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">nonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">head</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">tail</span> +<span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">cons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span> +<span class="nf">cons</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span> + +<span class="nf">tail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>Since the only way to construct or consume <code>NonEmpty</code> values is to use the functions in <code>Data.List.NonEmpty.Newtype</code>’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like <em>tokens</em>: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case <code>head</code> and <code>tail</code>, to obtain the values contained within.</p><p>This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid <code>NonEmpty []</code> value. For this reason, the newtype approach to type safety does not on its own constitute a <em>proof</em> that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a <code>Generic</code> instance for <code>NonEmpty</code>:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DeriveGeneric #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">GHC.Generics</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span></code></pre><p>However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">GHC</span><span class="o">.</span><span class="kt">Generics</span><span class="o">.</span><span class="n">to</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">K1</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>This is a particularly extreme example, since derived <code>Generic</code> instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived <code>Read</code> instance:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">read</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="s">"NonEmpty []"</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:</p><ul><li><p>All invariants must be made clear to maintainers of the trusted module. For simple types, such as <code>NonEmpty</code>, the invariant is self-evident, but for more sophisticated types, comments are not optional.</p></li><li><p>Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.</p></li><li><p>Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.</p></li><li><p>Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.</p></li></ul><p>In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.</p><p>In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.</p><h2><a name="other-newtype-use-abuse-and-misuse"></a>Other newtype use, abuse, and misuse</h2><p>The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:</p><ul><li><p>Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the <code>Sum</code> and <code>Product</code> newtypes from <code>Data.Monoid</code> provide useful <code>Monoid</code> instances for numeric types.</p></li><li><p>In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The <code>Flip</code> newtype from <code>Data.Bifunctor.Flip</code> is a simple example, flipping the arguments of a <code>Bifunctor</code> so the <code>Functor</code> instance may operate on the other side:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runFlip</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.</p></li><li><p>More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a <code>ByteString</code> containing a secret key may be wrapped in a newtype (with a <code>Show</code> instance omitted) to discourage code from accidentally logging or otherwise exposing it.</p></li></ul><p>All of these applications are good ones, but they have little to do with <em>type safety.</em> The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually <em>prevents</em> misuse; any part of the program may inspect the value at any time.</p><p>Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">unArgumentName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSONKey</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSONKey</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">Hashable</span><span class="p">,</span><span class="w"> </span><span class="kt">ToTxt</span><span class="p">,</span><span class="w"> </span><span class="kt">Lift</span><span class="p">,</span><span class="w"> </span><span class="kt">Generic</span><span class="p">,</span><span class="w"> </span><span class="kt">NFData</span><span class="p">,</span><span class="w"> </span><span class="kt">Cacheable</span><span class="w"> </span><span class="p">)</span></code></pre><p>This newtype is useless noise. Functionally, it is completely interchangeable with its underlying <code>Name</code> type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an <code>ArgumentName</code>, since the enclosing field name already makes its role clear.</p><p>Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:</p><ul><li><p>Primarily, types distinguish <em>functional</em> differences between values. A value of type <code>NonEmpty a</code> is <em>functionally</em> distinct from a value of type <code>[a]</code>, since it is fundamentally structurally different and permits additional operations. In this sense, types are <em>structural</em>; they describe what values <em>are</em> in the internal world of the programming language.</p></li><li><p>Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate <code>Distance</code> and <code>Duration</code> types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.</p></li></ul><p>Note that both these uses are <em>pragmatic</em>; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system <em>is</em> a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like <code>ArgumentName</code>.</p><p>If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use <code>Name</code>, but in situations where the different label adds genuine clarity, one can always use a type alias:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span></code></pre><p>Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.</p><h2><a name="final-thoughts-and-related-reading"></a>Final thoughts and related reading</h2><p>I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.</p><p>Newtypes are one particular mechanism of defining <em>wrapper types</em>, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.</p><p>The catalyst that got me to finally sit down and write this was the recently-published <a href="https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/">Tagged is not a Newtype</a>. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, <code>Tagged</code> <em>is</em> a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.</p><p>Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.</p><p>And a name is not type safety.</p><ol class="footnotes"><li id="footnote-1"><p>Admittedly rather unlikely given its name, but bear with me through the contrived example. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Types as axioms, or: playing god with static types2020-08-13T00:00:00Z2020-08-13T00:00:00ZAlexis King<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more open2020-01-19T00:00:00Z2020-01-19T00:00:00ZAlexis King<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Parse, don’t validate2019-11-05T00:00:00Z2019-11-05T00:00:00ZAlexis King<article><p>Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.</p><p>However, about a month ago, <a href="https://twitter.com/lexi_lambda/status/1182242561655746560">I was reflecting on Twitter</a> about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:</p><div style="text-align: center; font-size: larger"><strong>Parse, don’t validate.</strong></div><h2><a name="the-essence-of-type-driven-design"></a>The essence of type-driven design</h2><p>Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.</p><h3><a name="the-realm-of-possibility"></a>The realm of possibility</h3><p>One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:</p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Void</span></code></pre><p>Is it possible to implement <code>foo</code>? Trivially, the answer is <em>no</em>, as <code>Void</code> is a type that contains no values, so it’s impossible for <em>any</em> function to produce a value of type <code>Void</code>.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘head’: Patterns not matched: [] +</code></pre><p>This message is helpfully pointing out that our function is <em>partial</em>, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is <code>[]</code>, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.</p><h3><a name="turning-partial-functions-total"></a>Turning partial functions total</h3><p>To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the <code>head</code> function, and we’ll start with the simplest one.</p><h4><a name="managing-expectations"></a>Managing expectations</h4><p>As established, <code>head</code> is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the <code>Maybe</code> type:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span></code></pre><p>This buys us the freedom we need to implement <code>head</code>—it allows us to return <code>Nothing</code> when we discover we can’t produce a value of type <code>a</code> after all:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>Problem solved, right? For the moment, yes… but this solution has a hidden cost.</p><p>Returning <code>Maybe</code> is undoubtably convenient when we’re <em>implementing</em> <code>head</code>. However, it becomes significantly less convenient when we want to actually use it! Since <code>head</code> always has the potential to return <code>Nothing</code>, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">configDirsList</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">configDirsList</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">cacheDir</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="n">cacheDir</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"should never happen; already checked configDirs is non-empty"</span></code></pre><p>When <code>getConfigurationDirectories</code> retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use <code>head</code> in <code>main</code> to get the first element of the list, the <code>Maybe FilePath</code> result still requires us to handle a <code>Nothing</code> case that we know will never happen! This is terribly bad for several reasons:</p><ol><li><p>First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?</p></li><li><p>Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.</p></li><li><p>Finally, and worst of all, this code is a bug waiting to happen! What if <code>getConfigurationDirectories</code> were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update <code>main</code>, and suddenly the “impossible” error becomes not only possible, but probable.</p></li></ol><p>The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically <em>prove</em> the <code>Nothing</code> case impossible, then a modification to <code>getConfigurationDirectories</code> that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.</p><h4><a name="paying-it-forward"></a>Paying it forward</h4><p>Clearly, our modified version of <code>head</code> leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, <code>head</code> should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?</p><p>Let’s look at the original (partial) type signature for <code>head</code> again:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, <code>[a]</code>). Instead of weakening the return type, we can <em>strengthen</em> the argument type, eliminating the possibility of <code>head</code> ever being called on an empty list in the first place.</p><p>To do this, we need a type that represents non-empty lists. Fortunately, the existing <code>NonEmpty</code> type from <code>Data.List.NonEmpty</code> is exactly that. It has the following definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>Note that <code>NonEmpty a</code> is really just a tuple of an <code>a</code> and an ordinary, possibly-empty <code>[a]</code>. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the <code>[a]</code> component is <code>[]</code>, the <code>a</code> component must always be present. This makes <code>head</code> completely trivial to implement:<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Unlike before, GHC accepts this definition without complaint—this definition is <em>total</em>, not partial. We can update our program to use the new implementation:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">FilePath</span><span class="p">)</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">nonEmpty</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="p">)</span></code></pre><p>Note that the redundant check in <code>main</code> is now completely gone! Instead, we perform the check exactly once, in <code>getConfigurationDirectories</code>. It constructs a <code>NonEmpty a</code> from a <code>[a]</code> using the <code>nonEmpty</code> function from <code>Data.List.NonEmpty</code>, which has the following type:</p><pre><code class="pygments"><span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>The <code>Maybe</code> is still there, but this time, we handle the <code>Nothing</code> case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a <code>NonEmpty FilePath</code> value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type <code>NonEmpty a</code> as being like a value of type <code>[a]</code>, plus a <em>proof</em> that the list is non-empty.</p><p>By strengthening the type of the argument to <code>head</code> instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:</p><ul><li><p>The code has no redundant checks, so there can’t be any performance overhead.</p></li><li><p>Furthermore, if <code>getConfigurationDirectories</code> changes to stop checking that the list is non-empty, its return type must change, too. Consequently, <code>main</code> will fail to typecheck, alerting us to the problem before we even run the program!</p></li></ul><p>What’s more, it’s trivial to recover the old behavior of <code>head</code> from the new one by composing <code>head</code> with <code>nonEmpty</code>:</p><pre><code class="pygments"><span class="nf">head&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fmap</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">nonEmpty</span></code></pre><p>Note that the inverse is <em>not</em> true: there is no way to obtain the new version of <code>head</code> from the old one. All in all, the second approach is superior on all axes.</p><h3><a name="the-power-of-parsing"></a>The power of parsing</h3><p>You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:</p><pre><code class="pygments"><span class="nf">validateNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span> + +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="n">xs</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span></code></pre><p>These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: <code>validateNonEmpty</code> always returns <code>()</code>, the type that contains no information, but <code>parseNonEmpty</code> returns <code>NonEmpty a</code>, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but <code>parseNonEmpty</code> gives the caller access to the information it learned, while <code>validateNonEmpty</code> just throws it away.</p><p>These two functions elegantly illustrate two different perspectives on the role of a static type system: <code>validateNonEmpty</code> obeys the typechecker well enough, but only <code>parseNonEmpty</code> takes full advantage of it. If you see why <code>parseNonEmpty</code> is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of <code>parseNonEmpty</code>’s name. Is it really <em>parsing</em> anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe <code>parseNonEmpty</code> is a bona-fide parser (albeit a particularly simple one).</p><p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and <code>parseNonEmpty</code> is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.</p><p>Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:</p><ul><li><p>The <a href="https://hackage.haskell.org/package/aeson">aeson</a> library provides a <code>Parser</code> type that can be used to parse JSON data into domain types.</p></li><li><p>Likewise, <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> provides a set of parser combinators for parsing command-line arguments.</p></li><li><p>Database libraries like <a href="https://hackage.haskell.org/package/persistent">persistent</a> and <a href="https://hackage.haskell.org/package/postgresql-simple">postgresql-simple</a> have a mechanism for parsing values held in an external data store.</p></li><li><p>The <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.</p></li></ul><p>The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.</p><p>One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the <code>NonEmpty</code> example above: if the parsing and processing logic go out of sync, the program will fail to even compile.</p><h3><a name="the-danger-of-validation"></a>The danger of validation</h3><p>Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?</p><p>Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the <a href="http://langsec.org">language-theoretic security</a> field calls <em>shotgun parsing</em>. In the 2016 paper, <a href="http://langsec.org/papers/langsec-cwes-secdev2016.pdf">The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them</a>, its authors provide the following definition:</p><blockquote><p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.</p></blockquote><p>They go on to explain the problems inherent to such validation techniques:</p><blockquote><p>Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.</p></blockquote><p>In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.</p><p>It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.</p><p>Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p><h2><a name="parsing-not-validating-in-practice"></a>Parsing, not validating, in practice</h2><p>So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”</p><p>My advice: focus on the datatypes.</p><p>Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a <code>Map</code>. Adjust your function’s type signature to accept a <code>Map</code> instead of a list of tuples, and implement it as you normally would.</p><p>Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to <code>Map</code>, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of <code>checkNoDuplicateKeys</code>:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span></code></pre><p>Now the check <em>cannot</em> be omitted, since its result is actually necessary for the program to proceed!</p><p>This hypothetical scenario highlights two simple ideas:</p><ol><li><p><strong>Use a data structure that makes illegal states unrepresentable.</strong> Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.</p></li><li><p><strong>Push the burden of proof upward as far as possible, but no further.</strong> Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.</p></li></ol><p>In other words, write functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!</p><p>Here are a handful of additional points of advice, arranged in no particular order:</p><ul><li><p><strong>Let your datatypes inform your code, don’t let your code control your datatypes.</strong> Avoid the temptation to just stick a <code>Bool</code> in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.</p></li><li><p><strong>Treat functions that return <code>m ()</code> with deep suspicion.</strong> Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.</p></li><li><p><strong>Don’t be afraid to parse data in multiple passes.</strong> Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p></li><li><p><strong>Avoid denormalized representations of data, <em>especially</em> if it’s mutable.</strong> Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.</p><ul><li><p><strong>Keep denormalized representations of data behind abstraction boundaries.</strong> If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.</p></li></ul></li><li><p><strong>Use abstract datatypes to make validators “look like” parsers.</strong> Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract <code>newtype</code> with a smart constructor to “fake” a parser from a validator.</p></li></ul><p>As always, use your best judgement. It probably isn’t worth breaking out <a href="https://hackage.haskell.org/package/singletons">singletons</a> and refactoring your entire application just to get rid of a single <code>error "impossible"</code> call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.</p><h2><a name="recap-reflection-and-related-reading"></a>Recap, reflection, and related reading</h2><p>That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.</p><p>None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about <em>process</em>. My hope is that this is a small step in that direction.</p><p>Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post <a href="https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html">Type Safety Back and Forth</a>. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper <a href="https://kataskeue.com/gdp.pdf">Ghosts of Departed Proofs</a>, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.</p><p>As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit <em>any</em> value. These aren’t “real” values (unlike <code>null</code> in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that <a href="https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf">Fast and Loose Reasoning is Morally Correct</a>. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In fact, <code>Data.List.NonEmpty</code> already provides a <code>head</code> function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/types.rss.xml b/feeds/types.rss.xml new file mode 100644 index 0000000..11cbf62 --- /dev/null +++ b/feeds/types.rss.xml @@ -0,0 +1,558 @@ +Posts tagged ‘types’ | Alexis King’s BlogPosts tagged ‘types’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/types.html25 Mar 202125 Mar 202160An introduction to typeclass metaprogramminghttps://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/https://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/25 Mar 2021<article><p><em>Typeclass metaprogramming</em> is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem), and it is the core mechanism used to implement generic programming via <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">GHC generics</a>. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.</p><p>This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does <em>not</em> attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also <em>not</em> a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.</p><h2><a name="part-1-basic-building-blocks"></a>Part 1: Basic building blocks</h2><p>Typeclass metaprogramming is a big subject, which makes covering it in a blog post tricky. To break it into more manageable chunks, this post is divided into several parts, each of which introduces new type system features or type-level programming techniques, then presents an example of how they can be applied.</p><p>To start, we’ll cover the absolute foundations of typeclass metaprogramming.</p><h3><a name="typeclasses-as-functions-from-types-to-terms"></a>Typeclasses as functions from types to terms</h3><p>As its name implies, typeclass metaprogramming (henceforth TMP<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup>) centers around Haskell’s typeclass construct. Traditionally, typeclasses are viewed as a mechanism for principled operator overloading; for example, they underpin Haskell’s polymorphic <code>==</code> operator via the <code>Eq</code> class. Though that is often the most useful way to think about typeclasses, TMP encourages a different perspective: <strong>typeclasses are functions from types to (runtime) terms</strong>.</p><p>What does that mean? Let’s illustrate with an example. Suppose we define a typeclass called <code>TypeOf</code>:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>The idea is that this typeclass will accept some value and return the name of its type as a string. To illustrate, here are a couple potential instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Given these instances, we can observe that they do what we expect in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>Note that both the <code>TypeOf Bool</code> and <code>TypeOf Char</code> instances ignore the argument to <code>typeOf</code> altogether. This makes sense, as the whole point of the <code>TypeOf</code> class is to get access to <em>type</em> information, which is the same regardless of which value is provided. To make this more explicit, we can take advantage of some GHC extensions to eliminate the value-level argument altogether:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE AllowAmbiguousTypes, ScopedTypeVariables, TypeApplications #-}</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This typeclass definition is a little unusual, as the type parameter <code>a</code> doesn’t appear anywhere in the body. To understand what it means, recall that the type of each method of a typeclass is implicitly extended with the typeclass’s constraint. For example, in the definition</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>the full type of the <code>show</code> method is implicitly extended with a <code>Show a</code> constraint to yield:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>Furthermore, if we write <code>forall</code>s explicitly, each typeclass method is also implicitly quantified over the class’s type parameters, which makes the following the <em>full</em> type of <code>show</code>:</p><pre><code class="pygments"><span class="nf">show</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">Show</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>In the same vein, we can write out the full type of <code>typeOf</code>, as given by our new definition of <code>TypeOf</code>:</p><pre><code class="pygments"><span class="nf">typeOf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">String</span></code></pre><p>This type is still unusual, as the <code>a</code> type parameter doesn’t appear anywhere to the right of the <code>=&gt;</code> arrow. This makes the type parameter trivially <em>ambiguous</em>, which is to say it’s impossible for GHC to infer what <code>a</code> should be at any call site. Fortunately, <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_applications.html">we can use <code>TypeApplications</code></a> to pass a type for <code>a</code> directly, as we can see in the updated definition of <code>TypeOf (a, b)</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Bool"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Char"</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"("</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">", "</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">b</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">")"</span></code></pre><p>Once again, we can test out our new definitions in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="kt">Bool</span> +<span class="s">"Bool"</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Bool</span><span class="p">,</span><span class="w"> </span><span class="kt">Char</span><span class="p">)</span> +<span class="s">"(Bool, Char)"</span></code></pre><p>This illustrates very succinctly how typeclasses can be seen as functions from types to terms. Our <code>typeOf</code> function is, quite literally, a function that accepts a single type as an argument and returns a term-level <code>String</code>. Of course, the <code>TypeOf</code> typeclass is not a particularly <em>useful</em> example of such a function, but it demonstrates how easy it is to construct.</p><h3><a name="type-level-interpreters"></a>Type-level interpreters</h3><p>One important consequence of eliminating the value-level argument of <code>typeOf</code> is that there is no need for its argument type to actually be <em>inhabited</em>. For example, consider the <code>TypeOf</code> instance on <code>Void</code> from <code>Data.Void</code>:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="kt">Void</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"Void"</span></code></pre><p>This above instance is no different from the ones on <code>Bool</code> and <code>Char</code> even though <code>Void</code> is a completely uninhabited type. This is an important point: as we delve into type-level programming, it’s important to keep in mind that the language of types is mostly blind to the term-level meaning of those types. Although we usually write typeclasses that operate on values, this is not at all essential. This turns out to be quite important in practice, even in something as simple as the definition of <code>TypeOf</code> on lists:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">TypeOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"["</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="n">typeOf</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="o">++</span><span class="w"> </span><span class="s">"]"</span></code></pre><p>If <code>typeOf</code> required a value-level argument, not just a type, our instance above would be in a pickle when given the empty list, since it would have no value of type <code>a</code> to recursively apply <code>typeOf</code> to. But since <code>typeOf</code> only accepts a type-level argument, the term-level meaning of the list type poses no obstacle.</p><p>A perhaps unintuitive consequence of this property is that we can use typeclasses to write interesting functions on types even if none of the types are inhabited at all. For example, consider the following pair of type definitions:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Z</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="n">a</span></code></pre><p>It is impossible to construct any values of these types, but we can nevertheless use them to construct natural numbers at the type level:</p><ul><li><p><code>Z</code> is a type that represents 0.</p></li><li><p><code>S Z</code> is a type that represents 1.</p></li><li><p><code>S (S Z)</code> is a type that represents 2.</p></li></ul><p>And so on. These types might not seem very useful, since they aren’t inhabited by any values, but remarkably, we can still use a typeclass to distinguish them and convert them to term-level values:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Numeric.Natural</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>As its name implies, <code>reifyNat</code> reifies a type-level natural number encoded using our datatypes above into a term-level <code>Natural</code> value:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="kt">Z</span> +<span class="mi">0</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span> +<span class="mi">1</span> +<span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="mi">2</span></code></pre><p>One way to think about <code>reifyNat</code> is as an <em>interpreter</em> of a type-level language. In this case, the type-level language is very simple, only capturing natural numbers, but in general, it could be arbitrarily complex—and typeclasses can be used to give it a useful meaning, even if it has no term-level representation.</p><h3><a name="overlapping-instances"></a>Overlapping instances</h3><p>Generally, typeclass instances aren’t supposed to overlap. That is, if you write an instance for <code>Show (Maybe a)</code>, you aren’t supposed to <em>also</em> write an instance for <code>Show (Maybe Bool)</code>, since it isn’t clear whether <code>show (Just True)</code> should use the first instance or the second. For that reason, by default, GHC rejects any form of instance overlap as soon as it detects it.</p><p>Usually, this is the right behavior. Due to the way Haskell’s typeclass system is designed to preserve coherency—that is, the same combination of type arguments always selects the same instance—overlapping instances can be unintuitive or even cause nonsensical behavior if orphan instances are defined. However, when doing TMP, it’s useful to make exceptions to that rule of thumb, so GHC provides the option to explicitly opt-in to overlapping instances.</p><p>As a simple example, suppose we wanted to write a typeclass that checks whether a given type is <code>()</code> or not:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>If we were to write an ordinary, value-level function, we could write something like this pseudo-Haskell:</p><pre><code class="pygments"><span class="c1">-- not actually valid Haskell, just an example</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> +<span class="nf">isUnit</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>But if we try to translate this to typeclass instances, we’ll get a problem:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span></code></pre><p>The problem is that a function definition has a closed set of clauses matched from top to bottom, but typeclass instances are open and unordered.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> This means GHC will complain about instance overlap if we try to evaluate <code>isUnit @()</code>:</p><pre><code>ghci&gt; isUnit @() + +error: + • Overlapping instances for IsUnit () + arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance IsUnit () +</code></pre><p>To fix this, we have to explicitly mark <code>IsUnit ()</code> as overlapping:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>Now GHC accepts the expression without complaint:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="nb">()</span> +<span class="kt">True</span></code></pre><p>What does the <code>{-# OVERLAPPING #-}</code> pragma do, exactly? The gory details are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/instances.html#overlapping-instances">spelled out in the GHC User’s Guide</a>, but the simple explanation is that <code>{-# OVERLAPPING #-}</code> relaxes the overlap checker as long as the instance is <em>strictly more specific</em> than the instance(s) it overlaps with. In this case, that is true: <code>IsUnit ()</code> is trivially more specific than <code>IsUnit a</code>, since the former only matches <code>()</code> while the latter matches anything at all. That means our overlap is well-formed, and instance resolution should behave the way we’d like.</p><p>Overlapping instances are a useful tool when performing TMP, as they make it possible to write piecewise functions on types in the same way it’s possible to write piecewise functions on terms. However, they must still be used with care, as without understanding how they work, they can produce unintuitive results. For an example of how things can go wrong, consider the following definition:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>The intent of <code>guardUnit</code> is to use <code>isUnit</code> to detect if its argument is of type <code>()</code>, and if it is, to return an error. However, even though we marked <code>IsUnit ()</code> overlapping, we still get an overlapping instance error:</p><pre><code>error: + • Overlapping instances for IsUnit a arising from a use of ‘isUnit’ + Matching instances: + instance IsUnit a + instance [overlapping] IsUnit () + • In the expression: isUnit @a +</code></pre><p>What gives? The problem is that GHC simply doesn’t know what type <code>a</code> is when compiling <code>guardUnit</code>. It <em>could</em> be instantiated to <code>()</code> where it’s called, but it might not be. Therefore, GHC doesn’t know which instance to pick, and an overlapping instance error is still reported.</p><p>This behavior is actually a very, very good thing. If GHC were to blindly pick the <code>IsUnit a</code> instance in this case, then <code>guardUnit</code> would always take the <code>False</code> branch, even when passed a value of type <code>()</code>! That would certainly not be what was intended, so it’s better to reject this program than to silently do the wrong thing. However, in more complicated situations, it can be quite surprising that GHC is complaining about instance overlap even when <code>{-# OVERLAPPING #-}</code> annotations are used, so it’s important to keep their limitations in mind.</p><p>As it happens, in this particular case, the error is easily remedied. We simply have to add an <code>IsUnit</code> constraint to the type signature of <code>guardUnit</code>:</p><pre><code class="pygments"><span class="nf">guardUnit</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsUnit</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="n">a</span> +<span class="nf">guardUnit</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">isUnit</span><span class="w"> </span><span class="o">@</span><span class="n">a</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="s">"unit is not allowed"</span> +<span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">x</span></code></pre><p>Now picking the right <code>IsUnit</code> instance is deferred to the place where <code>guardUnit</code> is used, and the definition is accepted.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><h3><a name="type-families-are-functions-from-types-to-types"></a>Type families are functions from types to types</h3><p>In the previous section, we discussed how typeclasses are functions from types to terms, but what about functions from types to types? For example, suppose we wanted to sum two type-level natural numbers and get a new type-level natural number as a result? For that, we can use a type family:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE TypeFamilies #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>The above is a <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/type_families.html#closed-type-families">closed type family</a>, which works quite a lot like an ordinary Haskell function definition, just at the type level instead of at the value level. For comparison, the equivalent value-level definition of <code>Sum</code> would look like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="nf">sum</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nat</span> +<span class="nf">sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="nf">sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="n">sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>As you can see, the two are quite similar. Both are defined via a pair of pattern-matching clauses, and though it doesn’t matter here, both closed type families and ordinary functions evaluate their clauses top to bottom.</p><p>To test our definition of <code>Sum</code> in GHCi, we can use <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#ghci-cmd-:kind">the <code>:kind!</code> command</a>, which prints out a type and its kind after reducing it as much as possible:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span> +<span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">))</span></code></pre><p>We can also combine <code>Sum</code> with our <code>ReifyNat</code> class from earlier:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="kt">Z</span><span class="p">)))</span> +<span class="mi">3</span></code></pre><p>Type families are a useful complement to typeclasses when performing type-level programming. They allow computation to occur entirely at the type-level, which is necessarily computation that occurs entirely at compile-time, and the result can then be passed to a typeclass method to produce a term-level value from the result.</p><h3><a name="example-1-generalized-concat"></a>Example 1: Generalized <code>concat</code></h3><p>Finally, using what we’ve discussed so far, we can do our first bit of practical TMP. Specifically, we’re going to define a <code>flatten</code> function similar to like-named functions provided by many dynamically-typed languages. In those languages, <code>flatten</code> is like <code>concat</code>, but it works on a list of arbitrary depth. For example, we might use it like this:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]</span></code></pre><p>In Haskell, lists of different depths have different types, so multiple levels of <code>concat</code> have to be applied explicitly. But using TMP, we can write a generic <code>flatten</code> function that operates on lists of any depth!</p><p>Since this is <em>typeclass</em> metaprogramming, we’ll unsurprisingly begin with a typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="o">???</span><span class="p">]</span></code></pre><p>Our first challenge is writing the return type of <code>flatten</code>. Since the argument could be a list of any depth, there’s no direct way to obtain its element type. Fortunately, we can define a type family that does precisely that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="kt">ElementOf</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>Now we can write our <code>Flatten</code> instances. The base case is when the type is a list of depth 1, in which case we don’t have any flattening to do:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>The inductive case is when the type is a nested list, in which case we want to apply <code>concat</code> and recur:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">(</span><span class="n">concat</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Sadly, if we try to compile these definitions, GHC will reject our <code>Flatten [a]</code> instance:</p><pre><code>error: + • Couldn't match type ‘a’ with ‘ElementOf [a]’ + ‘a’ is a rigid type variable bound by + the instance declaration + Expected type: [ElementOf [a]] + Actual type: [a] + • In the expression: x + In an equation for ‘flatten’: flatten x = x + In the instance declaration for ‘Flatten [a]’ + | + | flatten x = x + | ^ +</code></pre><p>At first blush, this error looks very confusing. Why doesn’t GHC think <code>a</code> and <code>ElementOf [a]</code> are the same type? Well, consider what would happen if we picked a type like <code>[Int]</code> for <code>a</code>. Then <code>[a]</code> would be <code>[[Int]]</code>, a nested list, so the first case of <code>ElementOf</code> would apply. Therefore, GHC refuses to pick the second equation of <code>ElementOf</code> so hastily.</p><p>In this particular case, we might think that’s rather silly. After all, if <code>a</code> were <code>[Int]</code>, then GHC wouldn’t have picked the <code>Flatten [a]</code> instance to begin with, it would pick the more specific <code>Flatten [[a]]</code> instance defined below. Therefore, the hypothetical situation above could never happen. Unfortunately, GHC does not realize this, so we find ourselves at an impasse.</p><p>Fortunately, we can soothe GHC’s anxiety by adding an extra constraint to our <code>Flatten [a]</code> instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>This is a <em>type equality constraint</em>. Type equality constraints are written with the syntax <code>a ~ b</code>, and they state that <code>a</code> must be the same type as <code>b</code>. Type equality constraints are mostly useful when type families are involved, since they can be used (as in this case) to require a type family reduce to a certain type. In this case, we’re asserting that <code>ElementOf [a]</code> must always be <code>a</code>, which allows the instance to typecheck.</p><p>Note that this doesn’t let us completely wriggle out of our obligation, as the type equality constraint must <em>eventually</em> be checked when the instance is actually used, so initially this might seem like we’ve only deferred the problem to later. But in this case, that’s exactly what we need: by the time the <code>Flatten [a]</code> instance is selected, GHC will know that <code>a</code> is <em>not</em> a list type, and it will be able to reduce <code>ElementOf [a]</code> to <code>a</code> without difficulty. Indeed, we can see this for ourselves by using <code>flatten</code> in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="p">[[[</span><span class="mi">1</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">]],</span><span class="w"> </span><span class="p">[[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">6</span><span class="p">],</span><span class="w"> </span><span class="p">[</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">]]]</span> +<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">]</span></code></pre><p>It works! But why do we need the type annotation on <code>1</code>? If we leave it out, we get a rather hairy type error:</p><pre><code>error: + • Couldn't match type ‘ElementOf [a0]’ with ‘ElementOf [a]’ + Expected type: [ElementOf [a]] + Actual type: [ElementOf [a0]] + NB: ‘ElementOf’ is a non-injective type family + The type variable ‘a0’ is ambiguous +</code></pre><p>The issue here stems from the polymorphic nature of Haskell number literals. Theoretically, someone could define a <code>Num [a]</code> instance, in which case <code>1</code> could actually have a list type, and either case of <code>ElementOf</code> could match depending on the choice of <code>Num</code> instance. Of course, no such <code>Num</code> instance exists, nor should it, but the possibility of it being defined means GHC can’t be certain of the depth of the argument list.</p><p>This issue happens to come up a lot in simple examples of TMP, since polymorphic number literals introduce a level of ambiguity. In real programs, this is much less of an issue, since there’s no reason to call <code>flatten</code> on a completely hardcoded list! However, it’s still important to understand what these type errors mean and why they occur.</p><p>That wrinkle aside, <code>flatten</code> is a functioning example of what useful TMP can look like. We’ve written a single, generic definition that flattens lists of any depth, taking advantage of static type information to choose what to do at runtime.</p><h4><a name="typeclasses-as-compile-time-code-generation"></a>Typeclasses as compile-time code generation</h4><p>Presented with the above definition of <code>Flatten</code>, it might not be immediately obvious how to think about <code>Flatten</code> as a function from types to terms. After all, it looks a lot more like an “ordinary” typeclass (like, say, <code>Eq</code> or <code>Show</code>) than the <code>TypeOf</code> and <code>ReifyNat</code> classes we defined above.</p><p>One useful way to shift our perspective is to consider equivalent <code>Flatten</code> instances written using point-free style:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">ElementOf</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">id</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">Flatten</span><span class="w"> </span><span class="p">[[</span><span class="n">a</span><span class="p">]]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">flatten</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">concat</span></code></pre><p>These definitions of <code>flatten</code> no longer (syntactically) depend on term-level arguments, just like our definitions of <code>typeOf</code> and <code>reifyNat</code> didn’t accept any term-level arguments above. This allows us to consider what <code>flatten</code> might “expand to” given a type argument alone:</p><ul><li><p><code>flatten @[Int]</code> is just <code>id</code>, since the <code>Flatten [a]</code> instance is selected.</p></li><li><p><code>flatten @[[Int]]</code> is <code>flatten @[Int] . concat</code>, since the <code>Flatten [[a]]</code> instance is selected. That then becomes <code>id . concat</code>, which can be further simplified to just <code>concat</code>.</p></li><li><p><code>flatten @[[[Int]]]</code> is <code>flatten @[[Int]] . concat</code>, which simplifies to <code>concat . concat</code> by the same reasoning above.</p></li><li><p><code>flatten @[[[[Int]]]]</code> is then <code>concat . concat . concat</code>, and so on.</p></li></ul><p>This meshes quite naturally with our intuition of typeclasses as functions from types to terms. Each application of <code>flatten</code> takes a type as an argument and produces some number of composed <code>concat</code>s as a result. From this perspective, <code>Flatten</code> is performing a kind of compile-time code generation, synthesizing an expression to do the concatenation on the fly by inspecting the type information.</p><p>This framing is one of the key ideas that makes TMP so powerful, and indeed, it explains how it’s worthy of the name <em>metaprogramming</em>. As we continue to more sophisticated examples of TMP, try to keep this perspective in mind.</p><h2><a name="part-2-generic-programming"></a>Part 2: Generic programming</h2><p>Part 1 of this blog post established the foundational techniques used in TMP, all of which are useful on their own. If you’ve read up to this point, you now know enough to start applying TMP yourself, and the remainder of this blog post will simply continue to build upon what you already know.</p><p>In the previous section, we discussed how to use TMP to write a generic <code>flatten</code> operation. In this section, we’ll aim a bit higher: totally generic functions that operate on <em>arbitrary</em> datatypes.</p><h3><a name="open-type-families-and-associated-types"></a>Open type families and associated types</h3><p>Before we can dive into examples, we need to revisit type families. In the previous sections, we discussed closed type families, but we did not cover their counterpart, <em>open type families</em>. Like closed type families, open type families are effectively functions from types to types, but unlike closed type families, they are not defined with a predefined set of equations. Instead, new equations are added separately using <code>type instance</code> declarations. For example, we could define our <code>Sum</code> family from above like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">b</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Sum</span><span class="w"> </span><span class="p">(</span><span class="kt">S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="p">(</span><span class="kt">Sum</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span></code></pre><p>In the case of <code>Sum</code>, this would not be very useful, and indeed, <code>Sum</code> is much better expressed as a closed type family than an open one. But the advantage of open type families is similar to the advantage of typeclasses: new equations can be added at any time, even in modules other than the one that declares the open type family.</p><p>This extensibility means open type families are used less for type-level computation and more for type-level maps that associate types with other types. For example, one might define a <code>Key</code> open type family that relates types to the types used to index them:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="kr">type</span><span class="w"> </span><span class="kr">instance</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span></code></pre><p>This can be combined with a typeclass to provide a generic way to see if a data structure contains a given key:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>In this case, anyone could define their own data structure, define instances of <code>Key</code> and <code>HasKey</code> for their data structure, and use <code>hasKey</code> to see if it contains a given key, regardless of the structure of those keys. In fact, it’s so common for open type families and typeclasses to cooperate in this way that GHC provides the option to make the connection explicit by defining them together:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Vector</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="n">vec</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Vector</span><span class="o">.</span><span class="n">length</span><span class="w"> </span><span class="n">vec</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Map</span><span class="o">.</span><span class="n">member</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">HasKey</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Key</span><span class="w"> </span><span class="p">(</span><span class="kt">Trie</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ByteString</span> +<span class="w"> </span><span class="n">hasKey</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Data</span><span class="o">.</span><span class="kt">Trie</span><span class="o">.</span><span class="n">member</span></code></pre><p>An open family declared inside a typeclass like this is called an <em>associated type</em>. It works exactly the same way as the separate definitions of <code>Key</code> and <code>HasKey</code>, it just uses a different syntax. Note that although the <code>family</code> and <code>instance</code> keywords have disappeared from the declarations, that is only an abbreviation; the keywords are simply implicitly added (and explicitly writing them is still allowed, though most people do not).</p><p>Open type families and associated types are extremely useful for abstracting over similar types with slightly different structure, and libraries like <a href="https://hackage.haskell.org/package/mono-traversable"><code>mono-traversable</code></a> are examples of how they can be used to that end for their full effect. However, those use cases can’t really be classified as TMP, just using typeclasses for their traditional purpose of operation overloading.</p><p>However, that doesn’t mean open type families aren’t useful for TMP. In fact, one use case of TMP makes <em>heavy</em> use of open type families: datatype-generic programming.</p><h3><a name="example-2-datatype-generic-programming"></a>Example 2: Datatype-generic programming</h3><p><em>Datatype-generic programming</em> refers to a class of techniques for writing generic functions that operate on arbitrary data structures. Some useful applications of datatype-generic programming include</p><ul><li><p>equality, comparison, and hashing,</p></li><li><p>recursive traversal of self-similar data structures, and</p></li><li><p>serialization and deserialization,</p></li></ul><p>among other things. The idea is that by exploiting the structure of datatype definitions themselves, it’s possible for a datatype-generic function to provide implementations of functionality like the above on <em>any</em> datatype.</p><p>In Haskell, the most popular approach to datatype-generic programming leverages GHC generics, which is quite sophisticated. The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> already includes a fairly lengthy explanation of how it works, so I will not regurgitate it here (that could fill a blog post of its own!), but I will show how to construct a simplified version of the system that highlights the key role of TMP.</p><h4><a name="generic-datatype-representations"></a>Generic datatype representations</h4><p>At the heart of the <code>Generic</code> class is a simple concept: all non-GADT Haskell datatypes can be represented as sums of products. For example, if we have</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Authentication</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">AuthBasic</span><span class="w"> </span><span class="kt">Username</span><span class="w"> </span><span class="kt">Password</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AuthSSH</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>then we have a type that is essentially equivalent to this one:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span></code></pre><p>If we know how to define a function on a nested tree built out of <code>Either</code>s and pairs, then we know how to define it on <em>any</em> such datatype! This is where TMP comes in: recall the way we viewed <code>Flatten</code> as a mechanism for compile-time code generation based on type information. Could we use the same technique to generate implementations of equality, comparison, hashing, etc. from statically-known information about the structure of a datatype?</p><p>The answer to that question is <em>yes</em>. To start, let’s consider a particularly simple example: suppose we want to write a generic function that counts the number of fields stored in an arbitrary constructor. For example, <code>numFields (AuthBasic "alyssa" "pass1234")</code> would return <code>2</code>, while <code>numFields (AuthSSH "&lt;key&gt;")</code> would return <code>1</code>. Not a very useful function, admittedly, but it’s a simple example of what generic programming can do.</p><p>We’ll start by using TMP to implement a “generic” version of <code>numFields</code> that operates on trees of <code>Either</code>s and pairs as described above:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="c1">-- base case: leaf value</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="cm">{-# OVERLAPPING #-}</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>Just like our <code>Flatten</code> class from earlier, <code>GNumFields</code> uses the type-level structure of its argument to choose what to do:</p><ul><li><p>If we find a pair, that corresponds to a product, so we recur into both sides and sum the results.</p></li><li><p>If we find <code>Left</code> or <code>Right</code>, that corresponds to the “spine” differentiating different constructors, so we simply recur into the contained value.</p></li><li><p>In the case of any other value, we’re at a “leaf” in the tree of <code>Either</code>s and pairs, which corresponds to a single field, so we just return <code>1</code>.</p></li></ul><p>Now if we call <code>gnumFields (Left ("alyssa", "pass1234"))</code>, we’ll get <code>2</code>, and if we call <code>gnumFields (Right "&lt;key&gt;")</code>, we’ll get <code>1</code>. All that’s left to do is write a bit of code that converts our <code>Authentication</code> type to a tree of <code>Either</code>s and pairs:</p><pre><code class="pygments"><span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="nf">genericizeAuthentication</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFieldsAuthentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericizeAuthentication</span></code></pre><p>Now we get the results we want on our <code>Authentication</code> type using <code>numFieldsAuthentication</code>, but we’re not done yet, since it only works on <code>Authentication</code> values. Is there a way to define a generic <code>numFields</code> function that works on arbitrary datatypes that implement this conversion to sums-of-products? Yes, with another typeclass:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="kt">PublicKey</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="n">key</span> + +<span class="nf">numFields</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Rep</span><span class="w"> </span><span class="n">a</span><span class="p">))</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Natural</span> +<span class="nf">numFields</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">genericize</span></code></pre><p>Now <code>numFields (AuthBasic "alyssa" "pass1234")</code> returns <code>2</code>, as desired, and it will <em>also</em> work with any datatype that provides a <code>Generic</code> instance. If the above code makes your head spin, don’t worry: this is by far the most complicated piece of code in this blog post up to this point. Let’s break down how it works piece by piece:</p><ul><li><p>First, we define the <code>Generic</code> class, comprised of two parts:</p><ol><li><p>The <code>Rep a</code> associated type maps a type <code>a</code> onto its generic, sums-of-products representation, i.e. one built out of combinations of <code>Either</code> and pairs.</p></li><li><p>The <code>genericize</code> method converts an actual <em>value</em> of type <code>a</code> to the equivalent value using the sums-of-products representation.</p></li></ol></li><li><p>Next, we define a <code>Generic</code> instance for <code>Authentication</code>. <code>Rep Authentication</code> is the sums-of-products representation we described above, and <code>genericize</code> is likewise <code>genericizeAuthentication</code> from above.</p></li><li><p>Finally, we define <code>numFields</code> as a function with a <code>GNumFields (Rep a)</code> constraint. This is where all the magic happens:</p><ul><li><p>When we apply <code>numFields</code> to a datatype, <code>Rep</code> retrieves its generic, sums-of-products representation type.</p></li><li><p>The <code>GNumFields</code> class then uses various TMP techniques we’ve already described so far in this blog post to generate a <code>numFields</code> implementation on the fly from the structure of <code>Rep a</code>.</p></li><li><p>Finally, that generated <code>numFields</code> implementation is applied to the genericized term-level value, and the result is produced.</p></li></ul></li></ul><p>After all that, I suspect you might think this seems like a very convoluted way to define the (rather unhelpful) <code>numFields</code> operation. Surely just defining <code>numFields</code> on each type directly would be far easier? Indeed, if we were just considering <code>numFields</code>, you’d be right, but in fact we get much more than that. Using the same machinery, we can continue to define other generic operations—equality, comparison, etc.—the same way we defined <code>numFields</code>, and all of them would automatically work on <code>Authentication</code> because they all leverage the same <code>Generic</code> instance!</p><p>This is the basic value proposition of generic programming: we can do a little work up front to normalize our datatype to a generic representation <em>once</em>, then get a whole buffet of generic operations on it for free. In Haskell, the code generation capabilities of TMP is a key piece of that puzzle.</p><h4><a name="improving-our-definition-of-generic"></a>Improving our definition of <code>Generic</code></h4><p>You may note that the definition of <code>Generic</code> provided above does not match the one in <code>GHC.Generic</code>. Indeed, our naïve approach suffers from several flaws that the real version does not. This is not a <code>GHC.Generics</code> tutorial, so I will not discuss every detail of the full implementation, but I will highlight a few improvements relevant to the broader theme of TMP.</p><h5><a name="distinguishing-leaves-from-the-spine"></a>Distinguishing leaves from the spine</h5><p>One problem with our version of <code>Generic</code> is that it provides no way to distinguish an <code>Either</code> or pair that should be considered a “leaf”, as in a type like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">A</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">B</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">)</span></code></pre><p>Given this type, <code>Rep Foo</code> should be <code>Either (Either Int String) (Char, Bool)</code>, and <code>numFields (Right ('a', True))</code> will erroneously return <code>2</code> rather than <code>1</code>. To fix this, we can introduce a simple wrapper newtype that distinguishes leaves specifically:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">getLeaf</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Now our <code>Generic</code> instances look like this:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Authentication</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Username</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">Password</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="kt">PublicKey</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthBasic</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">user</span><span class="p">,</span><span class="w"> </span><span class="kt">Leaf</span><span class="w"> </span><span class="n">pass</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">AuthSSH</span><span class="w"> </span><span class="n">key</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">key</span><span class="p">)</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Foo</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">String</span><span class="p">))</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="p">(</span><span class="kt">Char</span><span class="p">,</span><span class="w"> </span><span class="kt">Bool</span><span class="p">))</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">A</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="p">(</span><span class="kt">B</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">x</span><span class="p">)</span></code></pre><p>Since the <code>Leaf</code> constructor now distinguishes a leaf, rather than the absence of an <code>Either</code> or <code>(,)</code> constructor, we’ll have to update our <code>GNumFields</code> instances as well. However, this has the additional pleasant effect of eliminating the need for overlapping instances:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Leaf</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Either</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Left</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">a</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="p">(</span><span class="kt">Right</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="n">b</span></code></pre><p>This is a good example of why overlapping instances can be so seductive, but they often have unintended consequences. Even when doing TMP, explicit tags are almost always preferable.</p><h5><a name="handling-empty-constructors"></a>Handling empty constructors</h5><p>Suppose we have a type with nullary data constructors, like the standard <code>Bool</code> type:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>How do we write a <code>Generic</code> instance for <code>Bool</code>? Using just <code>Either</code>, <code>(,)</code>, and <code>Leaf</code>, we can’t, but if we are willing to add a case for <code>()</code>, we can use it to denote nullary constructors:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">GNumFields</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">gnumFields</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Generic</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kr">type</span><span class="w"> </span><span class="kt">Rep</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Either</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Left</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="n">genericize</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Right</span><span class="w"> </span><span class="nb">()</span></code></pre><p>In a similar vein, we could use <code>Void</code> to represent datatypes that don’t have any constructors at all.</p><h4><a name="continuing-from-here"></a>Continuing from here</h4><p>The full version of <code>Generic</code> has a variety of further improvements useful for generic programming, including:</p><ul><li><p>Support for converting from <code>Rep a</code> to <code>a</code>.</p></li><li><p>Special indication of self-recursive datatypes, making generic tree traversals possible.</p></li><li><p>Type-level information about datatype constructor and record accessor names, allowing them to be used in serialization.</p></li><li><p>Fully automatic generation of <code>Generic</code> instances via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/generics.html#extension-DeriveGeneric">the <code>DeriveGeneric</code> extension</a>, which reduces the per-type boilerplate to essentially nothing.</p></li></ul><p>The <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">module documentation for <code>GHC.Generics</code></a> discusses the full system in detail, and it provides an additional example that uses the same essential TMP techniques discussed here.</p><h2><a name="part-3-dependent-typing"></a>Part 3: Dependent typing</h2><p>It’s time for the third and final part of this blog post: an introduction to dependently typed programming in Haskell. A full treatment of dependently typed programming is far, far too vast to be contained in a single blog post, so I will not attempt to do so here. Rather, I will cover some basic idioms for doing dependent programming and highlight how TMP can be valuable when doing so.</p><h3><a name="datatype-promotion"></a>Datatype promotion</h3><p>In part 1, we used uninhabited datatypes like <code>Z</code> and <code>S a</code> to define new type-level constants. This works, but it is awkward. Imagine for a moment that we wanted to work with type-level booleans. Using our previous approach, we could define two empty datatypes, <code>True</code> and <code>False</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">True</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">False</span></code></pre><p>Now we could define type families to provide operations on these types, such as <code>Not</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">True</span></code></pre><p>However, this has some frustrating downsides:</p><ul><li><p>First, it’s simply inconvenient that we have to define these new <code>True</code> and <code>False</code> “dummy” types, which are completely distinct from the <code>Bool</code> type provided by the prelude.</p></li><li><p>More significantly, it means <code>Not</code> has a very unhelpful kind:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="o">*</span></code></pre><p>Even though <code>Not</code> is only <em>supposed</em> to be applied to <code>True</code> or <code>False</code>, its kind allows it to be applied to any type at all. You can see this in practice if you try to evaluate something like <code>Not Char</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="o">!</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span> +<span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="o">*</span> +<span class="ow">=</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">Char</span></code></pre><p>Rather than getting an error, GHC simply spits <code>Not Char</code> back at us. This is a somewhat unintuitive property of closed type families: if none of the clauses match, the type family just gets “stuck,” not reducing any further. This can lead to very confusing type errors later in the typechecking process.</p></li></ul><p>One way to think about <code>Not</code> is that it is largely <em>dynamically kinded</em> in the same way some languages are dynamically typed. That isn’t entirely true, as we technically <em>will</em> get a kind error if we try to apply <code>Not</code> to a type constructor rather than a type, such as <code>Maybe</code>:</p><pre><code>ghci&gt; :kind! Not Maybe + +&lt;interactive&gt;:1:5: error: + • Expecting one more argument to ‘Maybe’ + Expected a type, but ‘Maybe’ has kind ‘* -&gt; *’ +</code></pre><p>…but <code>*</code> is still a very big kind, much bigger than we would like to permit for <code>Not</code>.</p><p>To help with both these problems, GHC provides <em>datatype promotion</em> via <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/data_kinds.html">the <code>DataKinds</code> language extension</a>. The idea is that for each normal, non-GADT type definition like</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">False</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">True</span></code></pre><p>then in addition to the normal type constructor and value constructors, GHC also defines several <em>promoted</em> constructors:</p><ul><li><p><code>Bool</code> is allowed as both a type and a kind.</p></li><li><p><code>'True</code> and <code>'False</code> are defined as new types of kind <code>Bool</code>.</p></li></ul><p>We can see this in action if we remove our <code>data True</code> and <code>data False</code> declarations and adjust our definition of <code>Not</code> to use promoted constructors:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DataKinds #-}</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;True</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;False</span> +<span class="w"> </span><span class="kt">Not</span><span class="w"> </span><span class="kt">&#39;False</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;True</span></code></pre><p>Now the inferred kind of <code>Not</code> is no longer <code>* -&gt; *</code>:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">kind</span><span class="w"> </span><span class="kt">Not</span> +<span class="kt">Not</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Bool</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span></code></pre><p>Consequently, we will now get a kind error if we attempt to apply <code>Not</code> to anything other than <code>'True</code> or <code>'False</code>:</p><pre><code>ghci&gt; :kind! Not Char + +&lt;interactive&gt;:1:5: error: + • Expected kind ‘Bool’, but ‘Char’ has kind ‘*’ +</code></pre><p>This is a nice improvement. We can make a similar change to our definitions involving type-level natural numbers:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Nat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Z</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">S</span><span class="w"> </span><span class="kt">Nat</span> + +<span class="kr">class</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Nat</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Natural</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="kt">&#39;Z</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ReifyNat</span><span class="w"> </span><span class="p">(</span><span class="kt">&#39;S</span><span class="w"> </span><span class="n">a</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">reifyNat</span><span class="w"> </span><span class="o">@</span><span class="n">a</span></code></pre><p>Note that we need to add an explicit kind signature on the definition of the <code>ReifyNat</code> typeclass, since otherwise GHC will assume <code>a</code> has kind <code>*</code>, since nothing in the types of the typeclass methods suggests otherwise. In addition to making it clearer that <code>Z</code> and <code>S</code> are related, this prevents someone from coming along and defining a nonsensical instance like <code>ReifyNat Char</code>, which previously would have been allowed but will now be rejected with a kind error.</p><p>Datatype promotion is not strictly required to do TMP, but makes the process significantly less painful. It makes Haskell’s kind language extensible in the same way its type language is, which allows type-level programming to enjoy static typechecking (or more accurately, static kindchecking) in the same way term-level programming does.</p><h3><a name="gadts-and-proof-terms"></a>GADTs and proof terms</h3><p>So far in this blog post, we have discussed several different function-like things:</p><ul><li><p>Ordinary Haskell functions are functions from terms to terms.</p></li><li><p>Type families are functions from types to types.</p></li><li><p>Typeclasses are functions from types to terms.</p></li></ul><p>A curious reader may wonder about the existence of a fourth class of function:</p><ul><li><p><em>???</em> are functions from terms to types.</p></li></ul><p>To reason about what could go in the <em>???</em> above, we must consider what “a function from terms to types” would even mean. Functions from terms to terms and types to types are straightforward enough. Functions from types to terms are a little trickier, but they make intuitive sense: we use information known at compile-time to generate runtime behavior. But how could information possibly flow in the other direction? How could we possibly turn runtime information into compile-time information without being able to predict the future?</p><p>In general, we cannot. However, one feature of Haskell allows a restricted form of seemingly doing the impossible—turning runtime information into compile-time information—and that’s GADTs.</p><p>GADTs<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup> are <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/exts/gadt.html">described in detail in the GHC User’s Guide</a>, but the key idea for our purposes is that <em>pattern-matching on a GADT constructor can refine type information</em>. Here’s a simple, silly example:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Bool</span> +<span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="kt">Int</span> + +<span class="nf">doSomething</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">x</span> +<span class="nf">doSomething</span><span class="w"> </span><span class="kt">AnInt</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span></code></pre><p>Here, <code>WhatIsIt</code> is a datatype with two nullary constructors, <code>ABool</code> and <code>AnInt</code>, similar to a normal, non-GADT datatype like this one:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">WhatIsIt</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ABool</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">AnInt</span></code></pre><p>What’s special about GADTs is that each constructor is given an explicit type signature. With the plain ADT definition above, <code>ABool</code> and <code>AnInt</code> would both have the type <code>forall a. WhatIsIt a</code>, but in the GADT definition, we explicitly fix <code>a</code> to <code>Bool</code> in the type of <code>ABool</code> and to <code>Int</code> in the type of <code>AnInt</code>.</p><p>This simple feature allows us to do very interesting things. The <code>doSomething</code> function is polymorphic in <code>a</code>, but on the right-hand side of the first equation, <code>x</code> has type <code>Bool</code>, while on the right-hand side of the second equation, <code>x</code> has type <code>Int</code>. This is because the <code>WhatIsIt a</code> argument effectively constrains the type of <code>a</code>, as we can see by experimenting with <code>doSomething</code> in GHCi:</p><pre><code>ghci&gt; doSomething ABool True +False +ghci&gt; doSomething AnInt 10 +11 +ghci&gt; doSomething AnInt True + +error: + • Couldn't match expected type ‘Int’ with actual type ‘Bool’ + • In the second argument of ‘doSomething’, namely ‘True’ + In the expression: doSomething AnInt True + In an equation for ‘it’: it = doSomething AnInt True +</code></pre><p>One way to think about GADTs is as “proofs” or “witnesses” of type equalities. The <code>ABool</code> constructor is a proof of <code>a ~ Bool</code>, while the <code>AnInt</code> constructor is a proof of <code>a ~ Int</code>. When you construct <code>ABool</code> or <code>AnInt</code>, you must be able to satisfy the equality, and it is in a sense “packed into” the constructor value. When code pattern-matches on the constructor, the equality is “unpacked from” the value, and the equality becomes available on the right-hand side of the pattern match.</p><p>GADTs can be much more sophisticated than our simple <code>WhatIsIt</code> type above. Just like normal ADTs, GADT constructors can have parameters, which makes it possible to write inductive datatypes that carry type equality proofs with them:</p><pre><code class="pygments"><span class="kr">infixr</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span> + +<span class="kr">data</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">HCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This type is a <em>heterogenous list</em>, a list that can contain elements of different types:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">:</span><span class="n">t</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Num</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[Bool, [Char]</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">]</span></code></pre><p>An <code>HList</code> is parameterized by a type-level list that keeps track of the types of its elements, which allows us to highlight another interesting property of GADTs: if we restrict that type information, the GHC pattern exhaustiveness checker will take the restriction into account. For example, we can write a completely total <code>head</code> function on <code>HList</code>s like this:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Remarkably, GHC does not complain that this definition of <code>head</code> is non-exhaustive. Since we specified that the argument must be of type <code>HList (a ': as)</code> in the type signature for <code>head</code>, GHC knows that the argument <em>cannot</em> be <code>HNil</code> (which would have the type <code>HList '[]</code>), so it doesn’t ask us to handle that case.</p><p>These examples illustrate the way GADTs serve as a general-purpose construct for relating type- and term-level information. Information flows bidirectionally: type information refines the set of type constructors that can be matched on, and matching on type constructors exposes new type equalities.</p><h4><a name="proofs-that-work-together"></a>Proofs that work together</h4><p>This interplay is wonderfully compositional. Suppose we wanted to write a function that accepts an <code>HList</code> of exactly 1, 2, or 3 elements. There’s no easy way to express that in the type signature the way we did with <code>head</code>, so it might seem like all we can do is write an entirely new container datatype that has three constructors, one for each case.</p><p>However, a more interesting solution exists that takes advantage of the bidirectional nature of GADTs. We can start by writing a <em>proof term</em> that contains no values, it just encapsulates type equalities on a type-level list:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a]</span> +<span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b]</span> +<span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="kt">&#39;[a, b, c]</span></code></pre><p>We call it a proof term because a value of type <code>OneToThree a b c as</code> constitutes a <em>proof</em> that <code>as</code> has exactly 1, 2, or 3 elements. Using <code>OneToThree</code>, we can write a function that accepts an <code>HList</code> accompanied by a proof term:</p><pre><code class="pygments"><span class="nf">sumUpToThree</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToThree</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span> +<span class="nf">sumUpToThree</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">z</span></code></pre><p>As with <code>head</code>, this function is completely exhaustive, in this case because we take full advantage of the bidirectional nature of GADTs:</p><ul><li><p>When we match on the <code>OneToThree</code> proof term, information flows from the term level to the type level, refining the type of <code>as</code> in that branch.</p></li><li><p>The refined type of <code>as</code> then flows back down to the term level, restricting the shape the <code>HList</code> can take and refinine the set of patterns we have to match.</p></li></ul><p>Of course, this example is not especially useful, but in general proof terms can encode any number of useful properties. For example, we can write a proof term that ensures an <code>HList</code> has an even number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>This is a proof which itself has inductive structure: <code>EvenCons</code> takes a proof that <code>as</code> has an even number of elements and produces a proof that adding two more elements preserves the evenness. We can combine this with a type family to write a function that “pairs up” elements in an <code>HList</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span> + +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Once again, this definition is completely exhaustive, and we can show that it works in GHCi:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="kt">EvenNil</span><span class="p">)</span> +<span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This ability to capture properties of a type using auxiliary proof terms, rather than having to define an entirely new type, is one of the things that makes dependently typed programming so powerful.</p><h4><a name="proof-inference"></a>Proof inference</h4><p>While our definition of <code>pairUp</code> is interesting, you may be skeptical of its practical utility. It’s fiddly and inconvenient to have to pass the <code>Even</code> proof term explicitly, since it must be updated every time the length of the list changes. Fortunately, this is where TMP comes in.</p><p>Remember that typeclasses are functions from types to terms. As its happens, a value of type <code>Even as</code> can be mechanically produced from the structure of the type <code>as</code>. This suggests that we could use TMP to automatically generate <code>Even</code> proofs, and indeed, we can. In fact, it’s not at all complicated:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>We can now adjust our <code>pairUp</code> function to use <code>IsEven</code> instead of an explicit <code>Even</code> argument:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">even</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">go</span><span class="w"> </span><span class="n">even</span><span class="w"> </span><span class="n">xs</span></code></pre><p>This is essentially identical to its old definition, but by acquiring the proof via <code>IsEven</code> rather than passing it explicitly, we can call <code>pairUp</code> without having to construct a proof manually:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="p">(</span><span class="kt">True</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="s">"foo"</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span> +<span class="p">(</span><span class="kt">True</span><span class="p">,</span><span class="sc">&#39;a&#39;</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="p">,</span><span class="s">"foo"</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>This is rather remarkable. Using TMP, we are able to get GHC to <em>automatically construct a proof that a list is even</em>, with no programmer guidance beyond writing the <code>IsEven</code> typeclass. This relies once more on the perspective that typeclasses are functions that accept types and generate term-level code: <code>IsEven</code> is a function that accepts a type-level list and generates an <code>Even</code> proof term.</p><p>From this perspective, <strong>typeclasses are a way of specifying a proof search algorithm</strong> to the compiler. In the case of <code>IsEven</code>, the proofs being generated are rather simple, so the proof search algorithm is quite mechanical. But in general, typeclasses can be used to perform proof search of significant complexity, given a sufficiently clever encoding into the type system.</p><h3><a name="aside-gadts-versus-type-families"></a>Aside: GADTs versus type families</h3><p>Before moving on, I want to explicitly call attention to the relationship between GADTs and type families. Though at first glance they may seem markedly different, there are some similarities between the two, and sometimes they may be used to accomplish similar things.</p><p>Consider again the type of the <code>pairUp</code> function above (without the typeclass for simplicity):</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span></code></pre><p>We used both a GADT, <code>Even</code>, and a type family, <code>PairUp</code>. But we could have, in theory, used <em>only</em> a GADT and eliminated the type family altogether. Consider this variation on the <code>Even</code> proof term:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kt">&#39;[]</span> +<span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="p">((</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span></code></pre><p>This type has two type parameters rather than one, and though there’s no distinction between the two from GHC’s point of view, it can be useful to think of <code>as</code> as an “input” parameter and <code>bs</code> as an “output” parameter. The idea is that any <code>EvenPairs</code> proof relates both an even-length list type and its paired up equivalent:</p><ul><li><p><code>EvenNil</code> has type <code>EvenPairs '[] '[]</code>,</p></li><li><p><code>EvenCons EvenNil</code> has type <code>EvenPairs '[a, b] '[(a, b)]</code>,</p></li><li><p><code>EvenCons (EvenCons EvenNil)</code> has type <code>EvenPairs '[a, b, c, d] '[(a, b), (c, d)]</code>,</p></li><li><p>…and so on.</p></li></ul><p>This allows us to reformulate our <code>pairUp</code> type signature this way:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">EvenPairs</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="n">bs</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">bs</span></code></pre><p>The definition is otherwise unchanged. The <code>PairUp</code> type family is completely gone, because now <code>EvenPairs</code> itself defines the relation. In this way, GADTs can be used like type-level functions!</p><p>The inverse, however, is not true, at least not directly: we cannot eliminate the GADT altogether and exclusively use type families. One way to attempt doing so would be to define a type family that returns a constraint rather than a type:</p><pre><code class="pygments"><span class="kr">import</span><span class="w"> </span><span class="nn">Data.Kind</span><span class="w"> </span><span class="p">(</span><span class="kt">Constraint</span><span class="p">)</span> + +<span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span></code></pre><p>The idea here is that <code>IsEvenTF as</code> produces a constraint can only be satisfied if <code>as</code> has an even number of elements, since that’s the only way it will eventually reduce to <code>()</code>, which in this case means the empty set of constraints, not the unit type (yes, the syntax for that is confusing). And in fact, it’s true that putting <code>IsEvenTF as =&gt;</code> in a type signature successfully restricts <code>as</code> to be an even-length list, but it doesn’t allow us to write <code>pairUp</code>. To see why, we can try the following definition:</p><pre><code class="pygments"><span class="nf">pairUp</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="p">(</span><span class="kt">PairUp</span><span class="w"> </span><span class="n">as</span><span class="p">)</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="kt">HNil</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> +<span class="nf">pairUp</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">pairUp</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Unlike the version using the GADT, this version of <code>pairUp</code> is not considered exhaustive:</p><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘pairUp’: Patterns not matched: HCons _ HNil +</code></pre><p>This is because type families don’t provide the same bidirectional flow of information that GADTs do, they’re only type-level functions. The constraint generated by <code>IsEvenTF</code> provides no term-level evidence about the shape of <code>as</code>, so we can’t branch on it the way we can branch on the <code>Even</code> GADT.<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> (In a sense, <code>IsEvenTF</code> is doing <a href="/blog/2019/11/05/parse-don-t-validate/">validation, not parsing</a>.)</p><p>For this reason, I caution against overuse of type families. Their simplicity is seductive, but all too often you pay for that simplicity with inflexibility. GADTs combined with TMP for proof inference can provide the best of both worlds: complete control over the term-level proof that gets generated while still letting the compiler do most of the work for you.</p><h3><a name="guiding-type-inference"></a>Guiding type inference</h3><p>So far, this blog post has given relatively little attention to type inference. That is in some part a testament to the robustness of GHC’s type inference algorithm: even when fairly sophisticated TMP is involved, GHC often manages to propagate enough type information that type annotations are rarely needed.</p><p>However, when doing TMP, it would be irresponsible to not at least consider the type inference properties of programs. Type inference is what drives the whole typeclass resolution process to begin with, so poor type inference can easily make your fancy TMP construction next to useless. To take advantage of GHC to the fullest extent, programs should proactively guide the typechecker to help it infer as much as possible as often as possible.</p><p>To illustrate what that can look like, suppose we want to use TMP to generate an <code>HList</code> full of <code>()</code> values of an arbitrary length:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">HNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Testing in GHCi, we can see it behaves as desired:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[(), (), ()]</span> +<span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span></code></pre><p>Now suppose we write a function that accepts a list containing exactly one element and returns it:</p><pre><code class="pygments"><span class="nf">unsingleton</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">HList</span><span class="w"> </span><span class="kt">&#39;[a]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">unsingleton</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="kt">HNil</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Naturally, we would expect these to compose without a hitch. If we write <code>unsingleton unitList</code>, our TMP should generate a list of length 1, and we should get back <code>()</code>. However, it may surprise you to learn that <em>isn’t</em>, in fact, what happens:<sup><a href="#footnote-6" id="footnote-ref-6-1">6</a></sup></p><pre><code>ghci&gt; unsingleton unitList + +error: + • Ambiguous type variable ‘a0’ arising from a use of ‘unitList’ + prevents the constraint ‘(UnitList '[a0])’ from being solved. + Probable fix: use a type annotation to specify what ‘a0’ should be. + These potential instances exist: + instance UnitList as =&gt; UnitList (() : as) +</code></pre><p>What went wrong? The type error says that <code>a0</code> is ambiguous, but it only lists a single matching <code>UnitList</code> instance—the one we want—so how can it be ambiguous which one to select?</p><p>The problem stems from the way we defined <code>UnitList</code>. When we wrote the instance</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="nb">()</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span></code></pre><p>we said the first element of the type-level list must be <code>()</code>, so there’s nothing stopping someone from coming along and defining another instance:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="kt">Int</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>In that case, GHC would have no way to know which instance to pick. Nothing in the type of <code>unsingleton</code> forces the element in the list to have type <code>()</code>, so both instances are equally valid. To hedge against this future possibility, GHC rejects the program as ambiguous from the start.</p><p>Of course, this isn’t what we want. The <code>UnitList</code> class is supposed to <em>always</em> return a list of <code>()</code> values, so how can we force GHC to pick our instance anyway? The answer is to play a trick:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="nb">()</span><span class="p">,</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">UnitList</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">unitList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span><span class="w"> </span><span class="p">`</span><span class="kt">HCons</span><span class="p">`</span><span class="w"> </span><span class="n">unitList</span></code></pre><p>Here we’ve changed the instance so that it has the shape <code>UnitList (a ': as)</code>, with a type variable in place of the <code>()</code>, but we also added an equality constraint that forces <code>a</code> to be <code>()</code>. Intuitively, you might think these two instances are completely identical, but in fact they are not! As proof, our example now typechecks:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">unsingleton</span><span class="w"> </span><span class="n">unitList</span> +<span class="nb">()</span></code></pre><p>To understand why, it’s important to understand how GHC’s typeclass resolution algorithm works. Let’s start by establishing some terminology. Note that every instance declaration has the following shape:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="o">&lt;</span><span class="n">constraints</span><span class="o">&gt;</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">C</span><span class="w"> </span><span class="o">&lt;</span><span class="n">types</span><span class="o">&gt;</span></code></pre><p>The part to the left of the <code>=&gt;</code> is known as the <em>instance context</em>, while the part to the right is known as the <em>instance head</em>. Now for the important bit: when GHC attempts to pick which typeclass instance to use to solve a typeclass constraint, <strong>only the instance head matters, and the instance context is completely ignored</strong>. Once GHC picks an instance, it commits to its choice, and only then does it consider the instance context.</p><p>This explains why our two <code>UnitList</code> instances behave differently:</p><ul><li><p>Given the instance head <code>UnitList (() ': as)</code>, GHC won’t select the instance unless it knows the first element of the list is <code>()</code>.</p></li><li><p>But given the instance head <code>UnitList (a ': as)</code>, GHC will pick the instance regardless of the type of the first element. All that matters is that the list is at least one element long.</p></li></ul><p>After the <code>UnitList (a ': as)</code> instance is selected, GHC attempts to solve the constraints in the instance context, including the <code>a ~ ()</code> constraint. This <em>forces</em> <code>a</code> to be <code>()</code>, resolving the ambiguity and allowing type inference to proceed.</p><p>This distinction might seem excessively subtle, but in practice it is enormously useful. It means you, the programmer, have direct control over the type inference process:</p><ul><li><p>If you put a type in the instance head, you’re asking GHC to figure out how to make the types match up by some other means. Sometimes that’s very useful, since perhaps you want that type to inform which instance to pick.</p></li><li><p>But if you put an equality constraint in the instance context, the roles are reversed: you’re saying to the compiler “you don’t tell me, I’ll tell <em>you</em> what type this is,” effectively giving you a role in type inference itself.</p></li></ul><p>From this perspective, typeclass instances with equality constraints make GHC’s type inference algorithm extensible. You get to pick which decisions are made and when, and crucially, you can use knowledge of your own program structure to expose more information to the typechecker.</p><p>Given all of the above, consider again the definition of <code>IsEven</code> from earlier:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Even</span><span class="w"> </span><span class="n">as</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Though it didn’t cause any problems in the examples we tried, this definition isn’t optimized for type inference. If GHC needed to solve an <code>IsEven (a ': b0)</code> constraint, where <code>b0</code> is an ambiguous type variable, it would get stuck, since it doesn’t know that someone won’t come along and define an <code>IsEven '[a]</code> instance in the future.</p><p>To fix this, we can apply the same trick we used for <code>UnitList</code>, just in a slightly different way:</p><pre><code class="pygments"><span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">bs</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="n">bs</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsEven</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">evenProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">evenProof</span></code></pre><p>Again, the idea is to move the type information we <em>learn</em> from picking this instance into the instance context, allowing it to guide type inference rather than making type inference figure it out from some other source. Consistently applying this transformation can <strong>dramatically</strong> improve type inference in programs that make heavy use of TMP.</p><h3><a name="example-3-subtyping-constraints"></a>Example 3: Subtyping constraints</h3><p>At last, we have reached the final example of this blog post. For this one, I have the pleasure of providing a real-world example from a production Haskell codebase: while I was working at <a href="https://hasura.io/">Hasura</a>, I had the opportunity to design an internal parser combinator library that captures aspects of the <a href="https://graphql.org/">GraphQL</a> type system. One such aspect of that type system is a form of subtyping; GraphQL essentially has two “kinds” of types—input types and output types—but some types can be used as both.</p><p>Haskell has no built-in support for subtyping, so most Haskell programs do their best to get away with parametric polymorphism instead. However, in our case, we actually need to distinguish (at runtime) types in the “both” category from those that are exclusively input or exclusively output types. Consequently, our <code>GQLKind</code> datatype has three cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLKind</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Both</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Input</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Output</span></code></pre><p>We use <code>DataKind</code>-promoted versions of this <code>GQLKind</code> type as a parameter to a <code>GQLType</code> GADT:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">TScalar</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Both</span> +<span class="w"> </span><span class="kt">TInputObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">InputObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Input</span> +<span class="w"> </span><span class="kt">TIObject</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">ObjectInfo</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLType</span><span class="w"> </span><span class="kt">&#39;Output</span> +<span class="w"> </span><span class="c1">-- ...and so on...</span></code></pre><p>This allows us to write functions that only accept input types or only accept output types, which is a wonderful property to be able to guarantee at compile-time! But there’s a problem: if we write a function that only accepts values of type <code>GQLType 'Input</code>, we can’t pass a <code>GQLType 'Both</code>, even though we really ought to be able to.</p><p>To fix this, we can use a little dependently typed programming. First, we’ll define a type to represent proof terms that witness a subkinding relationship:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">k</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span></code></pre><p>The first case, <code>KRefl</code>, states that every kind is trivially a subkind of itself. The second case, <code>KBoth</code>, states that <code>Both</code> is a subkind of any kind at all. (This is a particularly literal example of <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">using a type to define axioms</a>.) The next step is to use TMP to implement proof inference:</p><pre><code class="pygments"><span class="kr">class</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">SubKind</span><span class="w"> </span><span class="n">k1</span><span class="w"> </span><span class="n">k2</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KBoth</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span> + +<span class="kr">instance</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">KRefl</span></code></pre><p>These instances use the type equality trick described in the previous section to guide type inference, ensuring that if we ever need to prove that <code>k</code> is a superkind of <code>'Input</code> or <code>'Output</code>, type inference will force them to be equal.</p><p>Using <code>IsSubKind</code>, we can easily resolve the problem described above. Rather than write a function with a type like this:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>…we simply use an <code>IsSubKind</code> constraint, instead:</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now both <code>'Input</code> and <code>'Both</code> kinds are accepted. In my experience, this caused no trouble at all for callers of these functions; everything worked completely automatically. <em>Consuming</em> the <code>SubKind</code> proofs was slightly more involved, but only ever so slightly. For example, we have a type family that looks like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Both</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="kt">&#39;Output</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SelectionSet</span></code></pre><p>This type family is used to determine what a <code>GQLParser k a</code> actually consumes as input, based on the kind of the GraphQL type it corresponds to. In some functions, we need to prove to GHC that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>.</p><p>Fortunately, that is very easy to do using <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Type-Equality.html">the <code>(:~:)</code> type from <code>Data.Type.Equality</code> in <code>base</code></a> to capture a term-level witness of a type equality. It’s an ordinary Haskell GADT that happens to have an infix type constructor, and this is its definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="n">a</span></code></pre><p>Just as with any other GADT, <code>(:~:)</code> can be used to pack up type equalities and unpack them later; <code>a :~: b</code> just happens to be the GADT that corresponds precisely to the equality <code>a ~ b</code>. Using <code>(:~:)</code>, we can write a reusable proof that <code>IsSubKind k 'Input</code> implies <code>ParserInput k ~ InputValue</code>:</p><pre><code class="pygments"><span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">ParserInput</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">:~:</span><span class="w"> </span><span class="kt">InputValue</span> +<span class="nf">inputParserInput</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">subKindProof</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="o">@</span><span class="kt">&#39;Input</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">KRefl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span> +<span class="w"> </span><span class="kt">KBoth</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Refl</span></code></pre><p>This function is a very simple proof by cases, where <code>Refl</code> can be read as “Q.E.D.”:</p><ul><li><p>In the first case, matching on <code>KRefl</code> refines <code>k</code> to <code>'Input</code>, and <code>ParserInput 'Input</code> is <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li><li><p>Likewise, in the second case, matching on <code>KBoth</code> refines <code>k</code> to <code>'Both</code>, and <code>ParserInput 'Both</code> is also <code>InputValue</code> by definition of <code>ParserInput</code>.</p></li></ul><p>This <code>inputParserInput</code> helper allows functions like <code>nullable</code>, which internally need <code>ParserInput k ~ InputValue</code>, to take the form</p><pre><code class="pygments"><span class="nf">nullable</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">forall</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="o">.</span><span class="w"> </span><span class="kt">IsSubKind</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="kt">&#39;Input</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">GQLParser</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="p">(</span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nullable</span><span class="w"> </span><span class="n">parser</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">inputParserInput</span><span class="w"> </span><span class="o">@</span><span class="n">k</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Refl</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ...implementation goes here... -}</span></code></pre><p>Overall, this burden is quite minimal, so the additional type safety is more than worth the effort. The same could not be said without <code>IsSubKind</code> doing work to infer the proofs at each use site, so in this case, TMP has certainly paid its weight!</p><h2><a name="wrapping-up-and-closing-thoughts"></a>Wrapping up and closing thoughts</h2><p>So concludes my introduction to Haskell TMP. As seems to happen all too often with my blog posts, this one has grown rather long, so allow me to provide a summary of the most important points:</p><ul><li><p>Typeclass metaprogramming is a powerful technique for performing type-directed code generation, making it a form of “value inference” that infers values from types.</p></li><li><p>Unlike most other metaprogramming mechanisms, TMP has a wonderful synergy with type inference, which allows it to take advantage of information the programmer may not have even written explicitly.</p></li><li><p>Though I’ve called the technique “<em>typeclass</em> metaprogramming,” TMP really leverages the entirety of the modern GHC type system. Type families, GADTs, promoted types, and more all have their place in usefully applying type-level programming.</p></li><li><p>Finally, since TMP relies so heavily on type inference to do its job, it’s crucial to be thoughtful about how you design type-level code to give the typechecker as many opportunities to succeed as you possibly can.</p></li></ul><p>The individual applications of TMP covered in this blog post—type-level computation, generic programming, and dependent typing—are all useful in their own right, and this post does not linger on any of them long enough to do any of them justice. That is, perhaps, the cost one pays when trying to discuss such an abstract, general technique. However, I hope that readers can see the forest for the trees and understand how TMP can be a set of techniques in their own right, applicable to the topics described above and more.</p><p>Readers may note that this blog post targets a slightly different audience than my other recent writing has been. That is a conscious choice: there is an unfortunate dearth of resources to help intermediate Haskell programmers become advanced Haskell programmers, in part because it’s hard to write them. The lack of resources makes tackling topics like this rather difficult, as too often it feels as though an entire web of concepts must be explained all at once, with no obvious incremental path that provides sufficient motivation every step of the way.</p><p>It remains to be seen whether my stab at the problem will be successful. But on the chance that it is, I suspect some readers will be curious about where to go next. Here are some ideas:</p><ul><li><p>As mentioned earlier in this blog post, <a href="https://hackage.haskell.org/package/base-4.14.1.0/docs/GHC-Generics.html">the <code>GHC.Generics</code> module documentation</a> is a great resource if you want to explore generic programming further, and generic programming is a great way to put TMP to practical use.</p></li><li><p>I have long believed that <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/">the GHC User’s Guide</a> is a criminally under-read and underappreciated piece of documentation. It is a treasure trove of knowledge, and I highly recommend reading through the sections on type-related language extensions if you want to get a better grasp of the mechanics of the Haskell type system.</p></li><li><p>Finally, if dependently typed programming in Haskell intrigues you, and you don’t mind staring into the sun, the <a href="https://hackage.haskell.org/package/singletons">singletons</a> library provides abstractions and design patterns that can considerably cut down on the boilerplate. (Also, <a href="https://cs.brynmawr.edu/~rae/papers/2012/singletons/paper.pdf">the accompanying paper</a> is definitely worth a read if you’d like to go down that route.)</p></li></ul><p>Even if you don’t decide to pursue type-level programming in Haskell, I hope this blog post helps make some of the concepts involved less mystical and intimidating. I, for one, think this stuff is worth the effort involved in understanding. After all, you never know when it might come in handy.</p><ol class="footnotes"><li id="footnote-1"><p>Not to be confused with C++’s <a href="https://en.wikipedia.org/wiki/Template_metaprogramming"><em>template</em> metaprogramming</a>, though there are significant similarities between the two techniques. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>There have been proposals to introduce ordered instances, known in the literature as <a href="https://homepage.cs.uiowa.edu/~jgmorrs/pubs/morris-icfp2010-instances.pdf"><em>instance chains</em></a>, but as of this writing, GHC does not implement them. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Note that this also preserves an important property of the Haskell type system, parametricity. A function like <code>id :: a -&gt; a</code> shouldn’t be allowed to do different things depending on which type is chosen for <code>a</code>, which our first version of <code>guardUnit</code> tried to violate. Typeclasses, being functions on types, can naturally do different things given different types, so a typeclass constraint is precisely what gives us the power to violate parametricity. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Short for <em>generalized algebraic datatypes</em>, which is a rather unhelpful name for actually understanding what they are or what they’re for. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>If GHC allowed lightweight existential quantification, we could make that term-level evidence available with a sufficiently clever definition for <code>IsEvenTF</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kr">family</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Constraint</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="kt">&#39;[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="nb">()</span> +<span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">exists</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">as&#39;</span><span class="o">.</span><span class="w"> </span><span class="p">(</span><span class="n">as</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="p">(</span><span class="n">b</span><span class="w"> </span><span class="kt">&#39;:</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">),</span><span class="w"> </span><span class="kt">IsEvenTF</span><span class="w"> </span><span class="n">as&#39;</span><span class="p">)</span></code></pre><p>The type refinement provided by matching on <code>HCons</code> would be enough for the second case of <code>IsEvenTF</code> to be selected, which would provide an equality proof that <code>as</code> has at least two elements. Sadly, GHC does not support anything of this sort, and it’s unclear if it would be tractable to implement at all. <a href="#footnote-ref-5-1">↩</a></p></li><li id="footnote-6"><p>Actually, I’ve cheated a little bit here, because <code>unsingleton unitList</code> really does typecheck in GHCi under normal circumstances. That’s because <a href="https://downloads.haskell.org/ghc/9.0.1/docs/html/users_guide/ghci.html#extension-ExtendedDefaultRules">the <code>ExtendedDefaultRules</code> extension</a> is enabled in GHCi by default, which defaults ambiguous type variables to <code>()</code>, which happens to be exactly what’s needed to make this contrived example typecheck. However, that doesn’t say anything very useful, since the same expression really would fail to typecheck inside a Haskell module, so I’ve turned <code>ExtendedDefaultRules</code> off to illustrate the problem. <a href="#footnote-ref-6-1">↩</a></p></li></ol></article>Names are not type safetyhttps://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/01 Nov 2020<article><p>Haskell programmers spend a lot of time talking about <em>type safety</em>. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published <a href="/blog/2019/11/05/parse-don-t-validate/">Parse, Don’t Validate</a> as an initial stab towards bridging that gap.</p><p>The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s <code>newtype</code> construct. The idea is simple enough—the <code>newtype</code> keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this <em>sounds</em> like a simple and straightforward path to type safety. For example, one might consider using a <code>newtype</code> declaration to define a type for an email address:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EmailAddress</span><span class="w"> </span><span class="kt">Text</span></code></pre><p>This technique can provide <em>some</em> value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct <em>kind</em> of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.</p><p>And names are not type safety.</p><h2><a name="intrinsic-and-extrinsic-safety"></a>Intrinsic and extrinsic safety</h2><p>To illustrate the difference between constructive data modeling (discussed at length in my <a href="/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/">previous blog post</a>) and newtype wrappers, let’s consider an example. Suppose we want a type for “an integer between 1 and 5, inclusive.” The natural constructive modeling would be an enumeration with five cases:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">One</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Two</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Three</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Four</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Five</span></code></pre><p>We could then write some functions to convert between <code>Int</code> and our <code>OneToFive</code> type:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">One</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Two</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Three</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Four</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Five</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">1</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">2</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">3</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">4</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="mi">5</span></code></pre><p>This would be perfectly sufficient for achieving our stated goal, but you’d be forgiven for finding it odd: it would be rather awkward to work with in practice. Because we’ve invented an entirely new type, we can’t reuse any of the usual numeric functions Haskell provides. Consequently, many programmers would gravitate towards a newtype wrapper, instead:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="kt">Int</span></code></pre><p>Just as before, we can provide <code>toOneToFive</code> and <code>fromOneToFive</code> functions, with identical types:</p><pre><code class="pygments"><span class="nf">toOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">OneToFive</span> +<span class="nf">toOneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">otherwise</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> + +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Int</span> +<span class="nf">fromOneToFive</span><span class="w"> </span><span class="p">(</span><span class="kt">OneToFive</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">n</span></code></pre><p>If we put these declarations in their own module and choose not to export the <code>OneToFive</code> constructor, these APIs might appear entirely interchangeable. Naïvely, it seems that the newtype version is both simpler and equally type-safe. However—perhaps surprisingly—this is not actually true.</p><p>To see why, suppose we write a function that consumes a <code>OneToFive</code> value as an argument. Under the constructive modeling, such a function need only pattern-match against each of the five constructors, and GHC will accept the definition as exhaustive:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">One</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"first"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Two</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"second"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Three</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"third"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Four</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="kt">Five</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="s">"fifth"</span></code></pre><p>The same is not true given the newtype encoding. The newtype is opaque, so the only way to observe it is to convert it back to an <code>Int</code>—after all, it <em>is</em> an <code>Int</code>. An <code>Int</code> can of course contain many other values besides <code>1</code> through <code>5</code>, so we are forced to add an error case to satisfy the exhaustiveness checker:</p><pre><code class="pygments"><span class="nf">ordinal</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">OneToFive</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Text</span> +<span class="nf">ordinal</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromOneToFive</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"first"</span> +<span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"second"</span> +<span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"third"</span> +<span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fourth"</span> +<span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fifth"</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: bad OneToFive value"</span></code></pre><p>In this highly contrived example, this may not seem like much of a problem to you. But it nonetheless illustrates a key difference in the guarantees afforded by the two approaches:</p><ul><li><p>The constructive datatype captures its invariants in such a way that they are <em>accessible</em> to downstream consumers. This frees our <code>ordinal</code> function from worrying about handling illegal values, as they have been made unutterable.</p></li><li><p>The newtype wrapper provides a smart constructor that <em>validates</em> the value, but the boolean result of that check is used only for control flow; it is not preserved in the function’s result. Accordingly, downstream consumers cannot take advantage of the restricted domain; they are functionally accepting <code>Int</code>s.</p></li></ul><p>Losing exhaustiveness checking might seem like small potatoes, but it absolutely is not: our use of <code>error</code> has punched a hole right through our type system. If we were to add another constructor to our <code>OneToFive</code> datatype,<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> the version of <code>ordinal</code> that consumes a constructive datatype would be immediately detected non-exhaustive at compile-time, while the version that consumes a newtype wrapper would continue to compile yet fail at runtime, dropping through to the “impossible” case.</p><p>All of this is a consequence of the fact that the constructive modeling is <em>intrinsically</em> type-safe; that is, the safety properties are enforced by the type declaration itself. Illegal values truly are unrepresentable: there is simply no way to represent <code>6</code> using any of the five constructors. The same is not true of the newtype declaration, which has no intrinsic semantic distinction from that of an <code>Int</code>; its meaning is specified extrinsically via the <code>toOneToFive</code> smart constructor. Any semantic distinction intended by a newtype is thoroughly invisible to the type system; it exists only in the programmer’s mind.</p><h3><a name="revisiting-non-empty-lists"></a>Revisiting non-empty lists</h3><p>Our <code>OneToFive</code> datatype is rather artificial, but identical reasoning applies to other datatypes that are significantly more practical. Consider the <code>NonEmpty</code> datatype I’ve repeatedly highlighted in recent blog posts:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>It may be illustrative to imagine a version of <code>NonEmpty</code> represented as a newtype over ordinary lists. We can use the usual smart constructor strategy to enforce the desired non-emptiness property:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">Foldable</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span></code></pre><p>Just as with <code>OneToFive</code>, we quickly discover the consequences of failing to preserve this information in the type system. Our motivating use case for <code>NonEmpty</code> was the ability to write a safe version of <code>head</code>, but the newtype version requires another assertion:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">toList</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">x</span> +<span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>This might not seem like a big deal, since it seems unlikely such a case would ever happen. But that reasoning hinges entirely on trusting the correctness of the module that defines <code>NonEmpty</code>, while the constructive definition only requires trusting the GHC typechecker. As we generally trust that the typechecker works correctly, the latter is a much more compelling proof.</p><h2><a name="newtypes-as-tokens"></a>Newtypes as tokens</h2><p>If you are fond of newtypes, this whole argument may seem a bit troubling. It may seem like I’m implying newtypes are scarcely better than comments, albeit comments that happen to be meaningful to the typechecker. Fortunately, the situation is not quite that grim—newtypes <em>can</em> provide a sort of safety, just a weaker one.</p><p>The primary safety benefit of newtypes is derived from abstraction boundaries. If a newtype’s constructor is not exported, it becomes opaque to other modules. The module that defines the newtype—its “home module”—can take advantage of this to create a <em>trust boundary</em> where internal invariants are enforced by restricting clients to a safe API.</p><p>We can use the <code>NonEmpty</code> example from above to illustrate how this works. We refrain from exporting the <code>NonEmpty</code> constructor, and we provide <code>head</code> and <code>tail</code> operations that we trust to never actually fail:</p><pre><code class="pygments"><span class="kr">module</span><span class="w"> </span><span class="nn">Data.List.NonEmpty.Newtype</span> +<span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">NonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">cons</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">nonEmpty</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">head</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="nf">tail</span> +<span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="kr">where</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> + +<span class="nf">cons</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span> +<span class="nf">cons</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span> + +<span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span> +<span class="nf">nonEmpty</span><span class="w"> </span><span class="n">xs</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">xs</span> + +<span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span> + +<span class="nf">tail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="n">xs</span><span class="p">))</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">xs</span> +<span class="nf">tail</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"impossible: empty NonEmpty value"</span></code></pre><p>Since the only way to construct or consume <code>NonEmpty</code> values is to use the functions in <code>Data.List.NonEmpty.Newtype</code>’s exported API, the above implementation makes it impossible for clients to violate the non-emptiness invariant. In a sense, values of opaque newtypes are like <em>tokens</em>: the implementing module issues tokens via its constructor functions, and those tokens have no intrinsic value. The only way to do anything useful with them is to “redeem” them to the issuing module’s accessor functions, in this case <code>head</code> and <code>tail</code>, to obtain the values contained within.</p><p>This approach is significantly weaker than using a constructive datatype, since it is theoretically possible to screw up and accidentally provide a means to construct an invalid <code>NonEmpty []</code> value. For this reason, the newtype approach to type safety does not on its own constitute a <em>proof</em> that a desired invariant holds. However, it restricts the “surface area” where an invariant violation can occur to the defining module, so reasonable confidence the invariant really does hold can be achieved by thoroughly testing the module’s API using fuzzing or property-based testing techniques.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>This tradeoff may not seem all that bad, and indeed, it is often a very good one! Guaranteeing invariants using constructive data modeling can, in general, be quite difficult, which often makes it impractical. However, it is easy to dramatically underestimate the care needed to avoid accidentally providing a mechanism that permits violating the invariant. For example, the programmer may choose to take advantage of GHC’s convenient typeclass deriving to derive a <code>Generic</code> instance for <code>NonEmpty</code>:</p><pre><code class="pygments"><span class="cm">{-# LANGUAGE DeriveGeneric #-}</span> + +<span class="kr">import</span><span class="w"> </span><span class="nn">GHC.Generics</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span> + +<span class="kr">newtype</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Generic</span><span class="p">)</span></code></pre><p>However, this innocuous line provides a trivial mechanism to circumvent the abstraction boundary:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="kt">GHC</span><span class="o">.</span><span class="kt">Generics</span><span class="o">.</span><span class="n">to</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">M1</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="kt">K1</span><span class="w"> </span><span class="kt">[]</span><span class="p">)</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>This is a particularly extreme example, since derived <code>Generic</code> instances are fundamentally abstraction-breaking, but this problem can crop up in less obvious ways, too. The same problem occurs with a derived <code>Read</code> instance:</p><pre><code class="pygments"><span class="nf">ghci</span><span class="o">&gt;</span><span class="w"> </span><span class="n">read</span><span class="w"> </span><span class="o">@</span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="nb">()</span><span class="p">)</span><span class="w"> </span><span class="s">"NonEmpty []"</span> +<span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">[]</span></code></pre><p>To some readers, these pitfalls may seem obvious, but safety holes of this sort are remarkably common in practice. This is especially true for datatypes with more sophisticated invariants, as it may not be easy to determine whether the invariants are actually upheld by the module’s implementation. Proper use of this technique demands caution and care:</p><ul><li><p>All invariants must be made clear to maintainers of the trusted module. For simple types, such as <code>NonEmpty</code>, the invariant is self-evident, but for more sophisticated types, comments are not optional.</p></li><li><p>Every change to the trusted module must be carefully audited to ensure it does not somehow weaken the desired invariants.</p></li><li><p>Discipline is needed to resist the temptation to add unsafe trapdoors that allow compromising the invariants if used incorrectly.</p></li><li><p>Periodic refactoring may be needed to ensure the trusted surface area remains small. It is all too easy for the responsibility of the trusted module to accumulate over time, dramatically increasing the likelihood of some subtle interaction causing an invariant violation.</p></li></ul><p>In contrast, datatypes that are correct by construction suffer none of these problems. The invariant cannot be violated without changing the datatype definition itself, which has rippling effects throughout the rest of the program to make the consequences immediately clear. Discipline on the part of the programmer is unnecessary, as the typechecker enforces the invariants automatically. There is no “trusted code” for such datatypes, since all parts of the program are equally beholden to the datatype-mandated constraints.</p><p>In libraries, the newtype-afforded notion of safety via encapsulation is useful, as libraries often provide the building blocks used to construct more complicated data structures. Such libraries generally receive more scrutiny and care than application code does, especially given they change far less frequently. In application code, these techniques are still useful, but the churn of a production codebase tends to weaken encapsulation boundaries over time, so correctness by construction should be preferred whenever practical.</p><h2><a name="other-newtype-use-abuse-and-misuse"></a>Other newtype use, abuse, and misuse</h2><p>The previous section covers the primary means by which newtypes are useful. However, in practice, newtypes are routinely used in ways that do not fit the above pattern. Some such uses are reasonable:</p><ul><li><p>Haskell’s notion of typeclass coherency limits each type to a single instance of any given class. For types that permit more than one useful instance, newtypes are the traditional solution, and this can be used to good effect. For example, the <code>Sum</code> and <code>Product</code> newtypes from <code>Data.Monoid</code> provide useful <code>Monoid</code> instances for numeric types.</p></li><li><p>In a similar vein, newtypes can be useful for introducing or rearranging type parameters. The <code>Flip</code> newtype from <code>Data.Bifunctor.Flip</code> is a simple example, flipping the arguments of a <code>Bifunctor</code> so the <code>Functor</code> instance may operate on the other side:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Flip</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">runFlip</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">}</span></code></pre><p>Newtypes are needed to do this sort of juggling, as Haskell does not (yet) support type-level lambdas.</p></li><li><p>More simply, transparent newtypes can be useful to discourage misuse when the value needs to be passed between distant parts of the program and the intermediate code has no reason to inspect the value. For example, a <code>ByteString</code> containing a secret key may be wrapped in a newtype (with a <code>Show</code> instance omitted) to discourage code from accidentally logging or otherwise exposing it.</p></li></ul><p>All of these applications are good ones, but they have little to do with <em>type safety.</em> The last bullet in particular is often confused for safety, and to be fair, it does in fact take advantage of the type system to help avoid logical mistakes. However, it would be a mischaracterization to claim such usage actually <em>prevents</em> misuse; any part of the program may inspect the value at any time.</p><p>Too often, this illusion of safety leads to outright newtype abuse. For example, here’s a definition from the very codebase I work on for a living:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">unArgumentName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="kt">Show</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSONKey</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSONKey</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="kt">Hashable</span><span class="p">,</span><span class="w"> </span><span class="kt">ToTxt</span><span class="p">,</span><span class="w"> </span><span class="kt">Lift</span><span class="p">,</span><span class="w"> </span><span class="kt">Generic</span><span class="p">,</span><span class="w"> </span><span class="kt">NFData</span><span class="p">,</span><span class="w"> </span><span class="kt">Cacheable</span><span class="w"> </span><span class="p">)</span></code></pre><p>This newtype is useless noise. Functionally, it is completely interchangeable with its underlying <code>Name</code> type, so much so that it derives a dozen typeclasses! In every location it’s used, it’s immediately unwrapped the instant it’s extracted from its enclosing record, so there is no type safety benefit whatsoever. Worse, there isn’t even any clarity added by labeling it an <code>ArgumentName</code>, since the enclosing field name already makes its role clear.</p><p>Newtypes like these seem to arise from a desire to use the type system as a taxonomy of the external world. An “argument name” is a more specific concept than a generic “name,” so surely it ought to have its own type. This makes some intuitive sense, but it’s rather misguided: taxonomies are useful for documenting a domain of interest, but not necessarily helpful for modeling it. When programming, we use types for a different end:</p><ul><li><p>Primarily, types distinguish <em>functional</em> differences between values. A value of type <code>NonEmpty a</code> is <em>functionally</em> distinct from a value of type <code>[a]</code>, since it is fundamentally structurally different and permits additional operations. In this sense, types are <em>structural</em>; they describe what values <em>are</em> in the internal world of the programming language.</p></li><li><p>Secondarily, we sometimes use types to help ourselves avoid making logical mistakes. We might use separate <code>Distance</code> and <code>Duration</code> types to avoid accidentally doing something nonsensical like adding them together, even though they’re both representationally real numbers.</p></li></ul><p>Note that both these uses are <em>pragmatic</em>; they look at the type system as a tool. This is a rather natural perspective to take, seeing as a static type system <em>is</em> a tool in a literal sense. Nevertheless, that perspective seems surprisingly unusual, even though the use of types to classify the world routinely yields unhelpful noise like <code>ArgumentName</code>.</p><p>If a newtype is completely transparent, and it is routinely wrapped and unwrapped at will, it is likely not very helpful. In this particular case, I would eliminate the distinction altogether and use <code>Name</code>, but in situations where the different label adds genuine clarity, one can always use a type alias:<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">ArgumentName</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">GraphQL</span><span class="o">.</span><span class="kt">Name</span></code></pre><p>Newtypes like these are security blankets. Forcing programmers to jump through a few hoops is not type safety—trust me when I say they will happily jump through them without a second thought.</p><h2><a name="final-thoughts-and-related-reading"></a>Final thoughts and related reading</h2><p>I’ve been wanting to write this blog post for a long time. Ostensibly, it’s a very specific critique of Haskell newtypes, and I’ve chosen to frame things this way because I write Haskell for a living and this is the way I encounter this problem in practice. Really, though, the core idea is much bigger than that.</p><p>Newtypes are one particular mechanism of defining <em>wrapper types</em>, a concept that exists in almost any language, even those that are dynamically typed. Even if you don’t write Haskell, much of the reasoning in this blog post is likely still relevant in your language of choice. More broadly, this is a continuation of a theme I’ve been trying to convey from different angles over the past year: type systems are tools, and we should be more conscious and intentional about what they actually do and how to use them effectively.</p><p>The catalyst that got me to finally sit down and write this was the recently-published <a href="https://tech.freckle.com/2020/10/26/tagged-is-not-a-newtype/">Tagged is not a Newtype</a>. It’s a good blog post, and I wholeheartedly agree with its general thrust, but I thought it was a missed opportunity to make a larger point. Indeed, <code>Tagged</code> <em>is</em> a newtype, definitionally, so the title of the blog post is something of a misdirection. The real problem is a little deeper.</p><p>Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.</p><p>And a name is not type safety.</p><ol class="footnotes"><li id="footnote-1"><p>Admittedly rather unlikely given its name, but bear with me through the contrived example. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In theory, it is still possible to thoroughly prove the invariant holds using external verification techniques, such as by writing a pen-and-paper proof or by using program extraction in combination with a proof assistant/theorem prover. However, these techniques are extremely uncommon in general programming practice. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>As it happens, I think type aliases are often also more harmful than helpful, so I would caution against overusing them, too, but that is outside the scope of this blog post. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article>Types as axioms, or: playing god with static typeshttps://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/13 Aug 2020<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article>No, dynamic type systems are not inherently more openhttps://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/19 Jan 2020<article><p>Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.</p><p>This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are <em>not</em> about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.</p><h2><a name="two-typing-fallacies"></a>Two typing fallacies</h2><p>I’ve wanted to write this blog post for a while, but what finally made me decide to do it were misinformed comments responding to <a href="/blog/2019/11/05/parse-don-t-validate/">my previous blog post</a>. Two comments in particular caught my eye, <a href="https://www.reddit.com/r/programming/comments/dt0w63/parse_dont_validate/f6ulpsy/">the first of which was posted on /r/programming</a>:</p><blockquote><p>Strongly disagree with the post […] it promotes a fundamentally entangled and static view of the world. It assumes that we can or should theorize about what is "valid" input at the edge between the program and the world, thus introducing a strong sense of coupling through the entire software, where failure to conform to some schema will automatically crash the program.</p><p>This is touted as a feature here but imagine if the internet worked like this. A server changes their JSON output, and we need to recompile and reprogram the entire internet. This is the static view that is promoted as a feature here. […] The "parser mentality" is fundamentally rigid and global, whereas robust system design should be decentralised and leave interpretation of data to the receiver.</p></blockquote><p>Given the argument being made in the blog post—that you should use precise types whenever possible—one can see where this misinterpretation comes from. How could a proxy server possibly be written in such a style, since it cannot anticipate the structure of its payloads? The commenter’s conclusion is that strict static typing is at odds with programs that don’t know the structure of their inputs ahead of time.</p><p><a href="https://news.ycombinator.com/item?id=21479933">The second comment was left on Hacker News</a>, and it is significantly shorter than the first one:</p><blockquote><p>What would be the type signature of, say, Python's <code>pickle.load()</code>?</p></blockquote><p>This is a different kind of argument, one that relies on the fact that the types of reflective operations may depend on runtime values, which makes them challenging to capture with static types. This argument suggests that static types limit expressiveness because they forbid such operations outright.</p><p>Both these arguments are fallacious, but in order to show why, we have to make explicit an implicit claim. The two comments focus primarily on illustrating how static type systems can’t process data of an unknown shape, but they simultaneously advance an implicit belief: that dynamically typed languages <em>can</em> process data of an unknown shape. As we’ll see, this belief is misguided; programs are not capable of processing data of a truly unknown shape regardless of typing discipline, and static type systems only make already-present assumptions explicit.</p><h2><a name="you-can-t-process-what-you-don-t-know"></a>You can’t process what you don’t know</h2><p>The claim is simple: in a static type system, you must declare the shape of data ahead of time, but in a dynamic type system, the type can be, well, dynamic! It sounds self-evident, so much so that Rich Hickey has practically built a speaking career upon its emotional appeal. The only problem is it isn’t true.</p><p>The hypothetical scenario usually goes like this. Say you have a distributed system, and services in the system emit events that can be consumed by any other service that might need them. Each event is accompanied by a payload, which listening services can use to inform further action. The payload itself is minimally-structured, schemaless data encoded using a generic interchange format such as JSON or <a href="https://github.com/edn-format/edn">EDN</a>.</p><p>As a simple example, a login service might emit an event like this one whenever a new user signs up:</p><pre><code class="pygments"><span class="p">{</span> +<span class="w"> </span><span class="nt">"event_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"signup"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2020-01-19T05:37:09Z"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Alyssa"</span><span class="p">,</span> +<span class="w"> </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"alyssa@example.com"</span> +<span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Some downstream services might listen for these <code>signup</code> events and take further action whenever they are emitted. For example, a transactional email service might send a welcome email whenever a new user signs up. If the service were written in JavaScript, the handler might look something like this:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;login&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="s1">&#39;signup&#39;</span><span class="o">:</span> +<span class="w"> </span><span class="nx">sendEmail</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">email</span><span class="p">,</span><span class="w"> </span><span class="sb">`Welcome to Blockchain Emporium, </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">!`</span><span class="p">)</span> +<span class="w"> </span><span class="k">break</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>But what if this service were written in Haskell instead? Being good, reality-fearing Haskell programmers who <a href="/blog/2019/11/05/parse-don-t-validate/">parse, not validate</a>, the Haskell code might look something like this, instead:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span><span class="w"> </span><span class="p">}</span> +<span class="kr">data</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">SignupPayload</span> +<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Int</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Text</span><span class="w"> </span><span class="p">}</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">Event</span><span class="w"> </span><span class="kr">where</span> +<span class="w"> </span><span class="n">parseJSON</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">withObject</span><span class="w"> </span><span class="s">"Event"</span><span class="w"> </span><span class="nf">\</span><span class="n">obj</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"event_type"</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">eventType</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"login"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Login</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"data"</span><span class="p">)</span> +<span class="w"> </span><span class="s">"signup"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Signup</span><span class="w"> </span><span class="o">&lt;$&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="w"> </span><span class="o">.:</span><span class="w"> </span><span class="s">"signup"</span><span class="p">)</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"unknown event_type: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">eventType</span> + +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> +<span class="kr">instance</span><span class="w"> </span><span class="kt">FromJSON</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="kr">where</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span> + +<span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Login</span><span class="w"> </span><span class="kt">LoginPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userId</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Success</span><span class="w"> </span><span class="p">(</span><span class="kt">Signup</span><span class="w"> </span><span class="kt">SignupPayload</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">userName</span><span class="p">,</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="ow">-&gt;</span> +<span class="w"> </span><span class="n">sendEmail</span><span class="w"> </span><span class="n">userEmail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"Welcome to Blockchain Emporium, "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">userName</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="s">"!"</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="n">message</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"could not parse event: "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">message</span></code></pre><p>It’s definitely more boilerplate, but some extra overhead for type definitions is to be expected (and is greatly exaggerated in such tiny examples), and the arguments we’re discussing aren’t about boilerplate, anyway. The <em>real</em> problem with this version of the code, according to the Reddit comment from earlier, is that the Haskell code has to be updated whenever a service adds a new event type! A new case has to be added to the <code>Event</code> datatype, and it must be given new parsing logic. And what about when new fields get added to the payload? What a maintenance nightmare.</p><p>In comparison, the JavaScript code is much more permissive. If a new event type is added, it will just fall through the <code>switch</code> and do nothing. If extra fields are added to the payload, the JavaScript code will just ignore them. Seems like a win for dynamic typing.</p><p>Except that no, it isn’t. The only reason the statically typed program fails if we don’t update the <code>Event</code> type is that we wrote <code>handleEvent</code> that way. We could just have easily done the same thing in the JavaScript code, adding a default case that rejects unknown event types:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">({</span><span class="w"> </span><span class="nx">event_type</span><span class="p">,</span><span class="w"> </span><span class="nx">data</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="nx">event_type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="cm">/* ... */</span> +<span class="w"> </span><span class="k">default</span><span class="o">:</span> +<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`unknown event_type: </span><span class="si">${</span><span class="nx">event_type</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>We didn’t do that, since in this case it would clearly be silly. If a service receives an event it doesn’t know about, it should just ignore it. This is a case where being permissive is clearly the correct behavior, and we can easily implement that in the Haskell code too:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">fromJSON</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="cm">{- ... -}</span> +<span class="w"> </span><span class="kt">Error</span><span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span></code></pre><p>This is still in the spirit of “parse, don’t validate” because we’re still parsing the values we <em>do</em> care about as early as possible, so we don’t fall into the double-validation trap. At no point do we take a code path that depends on a value being well-formed without first ensuring (with the help of the type system) that it is, in fact, actually well-formed. We don’t have to respond to an ill-formed value by raising an error! We just have to be explicit about ignoring it.</p><p>This illustrates an important point: the <code>Event</code> type in this Haskell code doesn’t describe “all possible events,” it describes all the events that the application cares about. Likewise, the code that parses those events’ payloads only worries about the fields the application needs, and it ignores extraneous ones. A static type system doesn’t require you eagerly write a schema for the whole universe, it simply requires you to be up front about the things you need.</p><p>This turns out to have a lot of pleasant benefits even though knowledge about inputs is limited:</p><ul><li><p>It’s easy to discover the assumptions of the Haskell program just by looking at the type definitions. We know, for example, that this application doesn’t care about the <code>timestamp</code> field, since it never appears in any of the payload types. In the dynamically-typed program, we’d have to audit every code path to see whether or not it inspects that field, which would be a lot of error-prone work!</p></li><li><p>What’s more, it turns out the Haskell code doesn’t actually <em>use</em> the <code>userId</code> field inside the <code>SignupPayload</code> type, so that type is overly conservative. If we want to ensure it isn’t actually needed (since, for example, maybe we’re phasing out providing the user ID in that payload entirely), we need only delete that record field; if the code typechecks, we can be confident it really doesn’t depend on that field.</p></li><li><p>Finally, we neatly avoid all the gotchas related to shotgun parsing <a href="/blog/2019/11/05/parse-don-t-validate/#the-danger-of-validation">mentioned in the previous blog post</a>, since we still haven’t compromised on any of those principles.</p></li></ul><p>We’ve already invalidated the first half of the claim: that statically typed languages can’t deal with data where the structure isn’t completely known. Let’s now look at the other half, which states that dynamically typed languages can process data where the structure isn’t known at all. Maybe that still sounds right, but if you slow down and think about it more carefully, you’ll find it can’t be.</p><p>The above JavaScript code makes all the same assumptions our Haskell code does: it assumes event payloads are JSON objects with an <code>event_type</code> field, and it assumes <code>signup</code> payloads include <code>data.user.name</code> and <code>data.user.email</code> fields. It certainly can’t do anything useful with truly unknown input! If a new event payload is added, our JavaScript code can’t magically adapt to handle it simply because it is dynamically typed. Dynamic typing just means the types of values are carried alongside them at runtime and checked as the program executes; the types are still there, and this program still implicitly relies on them being particular things.</p><h2><a name="keeping-opaque-data-opaque"></a>Keeping opaque data opaque</h2><p>In the previous section, we debunked the idea that statically typed systems can’t process partially-known data, but if you have been paying close attention, you may have noticed it did not fully refute the original claim.</p><p>Although we were able to handle unknown data, we always simply discarded it, which would not fly if we were trying to implement some sort of proxying. For example, suppose we have a forwarding service that broadcasts events over a public network, attaching a signature to each payload to ensure it can’t be spoofed. We might implement this in JavaScript this way:</p><pre><code class="pygments"><span class="kd">const</span><span class="w"> </span><span class="nx">handleEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">signedPayload</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="nx">payload</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="nx">signature</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span><span class="w"> </span><span class="p">}</span> +<span class="w"> </span><span class="nx">retransmitEvent</span><span class="p">(</span><span class="nx">signedPayload</span><span class="p">)</span> +<span class="p">}</span></code></pre><p>In this case, we don’t care about the structure of the payload at all (the <code>signature</code> function just works on any valid JSON object), but we still have to preserve all the information. How could we do that in a statically typed language, since a statically-typed language would have to assign the payload a precise type?</p><p>Once again, the answer involves rejecting the premise: there’s no need to give data a type that’s any more precise than the application needs. The same logic could be written in a straightforward way in Haskell:</p><pre><code class="pygments"><span class="nf">handleEvent</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">JSON</span><span class="o">.</span><span class="kt">Value</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="p">(</span><span class="kt">Object</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">signedPayload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Map</span><span class="o">.</span><span class="n">insert</span><span class="w"> </span><span class="s">"signature"</span><span class="w"> </span><span class="p">(</span><span class="n">signature</span><span class="w"> </span><span class="n">payload</span><span class="p">)</span><span class="w"> </span><span class="n">payload</span> +<span class="w"> </span><span class="n">retransmitEvent</span><span class="w"> </span><span class="n">signedPayload</span> +<span class="nf">handleEvent</span><span class="w"> </span><span class="n">payload</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fail</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="s">"event payload was not an object "</span><span class="w"> </span><span class="o">&lt;&gt;</span><span class="w"> </span><span class="n">show</span><span class="w"> </span><span class="n">payload</span></code></pre><p>In this case, since we don’t care about the structure of the payload, we manipulate a value of type <code>JSON.Value</code> directly. This type is extremely imprecise compared to our <code>Event</code> type from earlier—it can hold any legal JSON value, of any shape—but in this case, we <em>want</em> it to be imprecise.</p><p>Thanks to that imprecision, the type system helped us here: it caught the fact that we’re assuming the payload is a JSON object, not some other JSON value, and it made us handle the non-object cases explicitly. In this case we chose to raise an error, but of course, as before, you could choose some other form of recovery if you wanted to. You just have to be explicit about it.</p><p>Once more, note that the assumption we were forced to make explicit in Haskell is <em>also</em> made by the JavaScript code! If our JavaScript <code>handleEvent</code> function were called with a string rather than an object, it’s unlikely the behavior would be desirable, since an object spread on a string results in the following surprise:</p><pre><code class="pygments"><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="s2">"payload"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="w"> </span><span class="p">}</span> +<span class="p">{</span><span class="mf">0</span><span class="o">:</span><span class="w"> </span><span class="s2">"p"</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="o">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="mf">3</span><span class="o">:</span><span class="w"> </span><span class="s2">"l"</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="o">:</span><span class="w"> </span><span class="s2">"o"</span><span class="p">,</span><span class="w"> </span><span class="mf">5</span><span class="o">:</span><span class="w"> </span><span class="s2">"a"</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="o">:</span><span class="w"> </span><span class="s2">"d"</span><span class="p">,</span><span class="w"> </span><span class="nx">signature</span><span class="o">:</span><span class="w"> </span><span class="s2">"sig"</span><span class="p">}</span></code></pre><p>Oops. Once again, the parsing style of programming has helped us out, since if we didn’t “parse” the JSON value into an object by matching on the <code>Object</code> case explicitly, our code would not compile, and if we left off the fallthrough case, we’d get a warning about inexhaustive patterns.</p><hr/><p>Let’s look at one more example of this phenomenon before moving on. Suppose we’re consuming an API that returns user IDs, and suppose those IDs happen to be UUIDs. A straightforward interpretation of “parse, don’t validate” might suggest we represent user IDs in our Haskell API client using a <code>UUID</code> type:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UUID</span></code></pre><p>However, our Reddit commenter would likely take umbrage with this! Unless the API contract explicitly states that all user IDs will be UUIDs, this representation is overstepping our bounds. Although user IDs might be UUIDs today, perhaps they won’t be tomorrow, and then our code would break for no reason! Is this the fault of static type systems?</p><p>Again, the answer is no. This is a case of improper data modeling, but the static type system is not at fault—it has simply been misused. The appropriate way to represent a <code>UserId</code> is to define a new, opaque type:</p><pre><code class="pygments"><span class="kr">newtype</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">UserId</span><span class="w"> </span><span class="kt">Text</span> +<span class="w"> </span><span class="kr">deriving</span><span class="w"> </span><span class="p">(</span><span class="kt">Eq</span><span class="p">,</span><span class="w"> </span><span class="kt">FromJSON</span><span class="p">,</span><span class="w"> </span><span class="kt">ToJSON</span><span class="p">)</span></code></pre><p>Unlike the type alias defined above which simply creates a new name for the existing <code>UUID</code> type, this declaration creates a totally new <code>UserId</code> type that is distinct from all other types, including <code>Text</code>. If we keep the datatype’s constructor private (that is, we don’t export it from the module that defines this type), then the <em>only</em> way to produce a <code>UserId</code> will be to go through its <code>FromJSON</code> parser. Dually, the only things you can do with a <code>UserId</code> are compare it with other <code>UserId</code>s for equality or serialize it using the <code>ToJSON</code> instance. Nothing else is permitted: the type system will prevent you from depending on the remote service’s internal representation of user IDs.</p><p>This illustrates another way that static type systems can provide strong, useful guarantees when manipulating completely opaque data. The runtime representation of a <code>UserId</code> is really just a string, but the type system does not allow you to accidentally use it like it’s a string, nor does it allow you to forge a new <code>UserId</code> out of thin air from an arbitrary string.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup></p><p>The type system is not a ball and chain forcing you to describe the representation of every value that enters and leaves your program in exquisite detail. Rather, it’s a tool that you can use in whatever way best suits your needs.</p><h2><a name="reflection-is-not-special"></a>Reflection is not special</h2><p>We’ve now thoroughly debunked the claims made by the first commenter, but the question posed by the second commenter may still seem like a loophole in our logic. What <em>is</em> the type of Python’s <code>pickle.load()</code>? For those unfamiliar, <a href="https://docs.python.org/3/library/pickle.html">Python’s cutely-named <code>pickle</code> library</a> allows serializing and deserializing entire Python object graphs. Any object can be serialized and stored in a file using <code>pickle.dump()</code>, and it can be deserialized at a later point in time using <code>pickle.load()</code>.</p><p>What makes this appear challenging to our static type system is that the type of value produced by <code>pickle.load()</code> is difficult to predict—it depends entirely on whatever happened to be written to that file using <code>pickle.dump()</code>. This seems inherently dynamic, since we cannot possibly know what type of value it will produce at compile-time. At first blush, this is something a dynamically typed system can pull off, but a statically-typed one just can’t.</p><p>However, it turns out this situation is actually identical to the previous examples using JSON, and the fact that Python’s pickling serializes native Python objects directly does not change things. Why? Well, consider what happens <em>after</em> a program calls <code>pickle.load()</code>. Say you write the following function:</p><pre><code class="pygments"><span class="k">def</span> <span class="nf">load_value</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> + <span class="n">val</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> + <span class="c1"># do something with `val`</span></code></pre><p>The trouble is that <code>val</code> can now be of <em>any</em> type, and just as you can’t do anything useful with truly unknown, unstructured input, you can’t do anything with a value unless you know at least something about it. If you call any method or access any field on the result, then you’ve already made an assumption about what sort of thing <code>pickle.load(f)</code> returned—and it turns out those assumptions <em>are</em> <code>val</code>’s type!</p><p>For example, imagine the only thing you do with <code>val</code> is call the <code>val.foo()</code> method and return its result, which is expected to be a string. If we were writing Java, then the expected type of <code>val</code> would be quite straightforward—we’d expect it to be an instance of the following interface:</p><pre><code class="pygments"><span class="kd">interface</span> <span class="nc">Foo</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">foo</span><span class="p">();</span> +<span class="p">}</span></code></pre><p>And indeed, it turns out a <code>pickle.load()</code>-like function can be given a perfectly reasonable type in Java:</p><pre><code class="pygments"><span class="kd">static</span><span class="w"> </span><span class="o">&lt;</span><span class="n">T</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">Serializable</span><span class="o">&gt;</span><span class="w"> </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">load</span><span class="p">(</span><span class="n">InputStream</span><span class="w"> </span><span class="n">in</span><span class="p">,</span><span class="w"> </span><span class="n">Class</span><span class="o">&lt;?</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="n">cls</span><span class="p">);</span></code></pre><p>Nitpickers will complain that this isn’t the same as <code>pickle.load()</code>, since you have to pass a <code>Class&lt;T&gt;</code> token to choose what type of thing you want ahead of time. However, nothing is stopping you from passing <code>Serializable.class</code> and branching on the type later, after the object has been loaded. And that’s the key point: the instant you do <em>anything</em> with the object, you must know something about its type, even in a dynamically typed language! The statically-typed language just forces you to be more explicit about it, just as it did when we were talking about JSON payloads.</p><hr/><p>Can we do this in Haskell, too? Absolutely—we can use <a href="https://hackage.haskell.org/package/serialise">the <code>serialise</code> library</a>, which has a similar API to the Java one mentioned above. It also happens to have a very similar interface to <a href="https://hackage.haskell.org/package/aeson">the Haskell JSON library, aeson</a>, as it turns out the problem of dealing with unknown JSON data is not terribly different from dealing with an unknown Haskell value—at some point, you have to do a little bit of parsing to do anything with the value.</p><p>That said, while you <em>can</em> emulate the dynamic typing of <code>pickle.load()</code> if you really want to by deferring the type check until the last possible moment, the reality is that doing so is almost never actually useful. At some point, you have to make assumptions about the structure of the value in order to use it, and you know what those assumptions are because <em>you wrote the code</em>. While there are extremely rare exceptions to this that require true dynamic code loading (such as, say, implementing a REPL for your programming language), they do not occur in day-to-day programming, and programmers in statically-typed languages are perfectly happy to supply their assumptions up front.</p><p>This is one of the fundamental disconnects between the static typing camp and the dynamic typing camp. Programmers working in statically-typed languages are perplexed when a programmer suggests they can do something in a dynamically typed language that a statically-typed language “fundamentally” prevents, since a programmer in a statically-typed language may reply the value has simply not been given a sufficiently precise type. From the perspective of a programmer working in a dynamically-typed language, the type system restricts the space of legal behaviors, but from the perspective of a programmer working in a statically-typed language, the set of legal behaviors <em>is</em> a value’s type.</p><p>Neither of these perspectives are actually inaccurate, from the appropriate point of view. Static type systems <em>do</em> impose restrictions on program structure, as it is provably impossible to reject <em>all</em> bad programs in a Turing-complete language without also rejecting some good ones (this is <a href="https://en.wikipedia.org/wiki/Rice's_theorem">Rice’s theorem</a>). But it is simultaneously true that the impossibility of solving the general problem does not preclude solving a slightly more restricted version of the problem in a useful way, and a lot of the so-called “fundamental” inabilities of static type systems are not fundamental at all.</p><h2><a name="appendix-the-reality-behind-the-myths"></a>Appendix: the reality behind the myths</h2><p>The key thesis of this blog post has now been delivered: static type systems are not fundamentally worse than dynamic type systems at processing data with an open or partially-known structure. The sorts of claims made in the comments cited at the beginning of this blog post are not accurate depictions of what statically-typed program construction is like, and they misunderstand the limitations of static typing disciplines while exaggerating the capabilities of dynamically typed disciplines.</p><p>However, although greatly exaggerated, these myths do have some basis in reality. They appear to have developed at least in part from a misunderstanding about the differences between structural and nominal typing. This difference is unfortunately too big to address in this blog post, as it could likely fill several blog posts of its own. About six months ago I attempted to write a blog post on the subject, but I didn’t think it came out very compelling, so I scrapped it. Maybe someday I’ll find a better way to communicate the ideas.</p><p>Although I can’t give it the full treatment it deserves right now, I’d still like to touch on the idea briefly so that interested readers may be able to find other resources on the subject should they wish to do so. The key idea is that many dynamically typed languages idiomatically reuse simple data structures like hashmaps to represent what in statically-typed languages are often represented by bespoke datatypes (usually defined as classes or structs).</p><p>These two styles facilitate very different flavors of programming. A JavaScript or Clojure program may represent a record as a hashmap from string or symbol keys to values, written using object or hash literals and manipulated using ordinary functions from the standard library that manipulate keys and values in a generic way. This makes it straightforward to take two records and union their fields or to take an arbitrary (or even dynamic) subselection of fields from an existing record.</p><p>In contrast, most static type systems do not allow such free-form manipulation of records because records are not maps at all but unique types distinct from all other types. These types are uniquely identified by their (fully-qualified) name, hence the term <em>nominal typing</em>. If you wish to take a subselection of a struct’s fields, you must define an entirely new struct; doing this often creates an explosion of awkward boilerplate.</p><p>This is one of the main ideas that Rich Hickey has discussed in many of his talks that criticize static typing. He has advanced the idea that this ability to fluidly merge, separate, and transform records makes dynamic typing particularly suited to the domain of distributed, open systems. Unfortunately, this rhetoric has two significant flaws:</p><ol><li><p>It skirts too close to calling this a fundamental limitation of type systems, suggesting that it is not simply inconvenient but <em>impossible</em> to model such systems in a nominal, static type system. Not only is this not true (as this blog post has demonstrated), it misdirects people away from the point of his that actually has value: the practical, pragmatic advantage of a more structural approach to data modeling.</p></li><li><p>It confuses the structural/nominal distinction with the dynamic/static distinction, incorrectly creating the impression that the fluid merging and splitting of records represented as key-value maps is only possible in a dynamically typed language. In fact, not only can statically-typed languages support structural typing, many dynamically-typed languages also support nominal typing. These axes have historically loosely correlated, but they are theoretically orthogonal.</p></li></ol><p>For counterexamples to these claims, consider Python classes, which are quite nominal despite being dynamic, and TypeScript interfaces, which are structural despite being static. Indeed, modern statically-typed languages are increasingly acquiring native support for structurally-typed records. In these systems, record types work much like hashes in Clojure—they are not distinct, named types but rather anonymous collections of key-value pairs—and they support many of the same expressive manipulation operations that Clojure’s hashes do, all within a statically-typed framework.</p><p>If you are interested in exploring static type systems with strong support for structural typing, I would recommend taking a look at any of TypeScript, Flow, PureScript, Elm, OCaml, or Reason, all of which have some sort of support for structurally typed records. What I would <em>not</em> recommend for this purpose is Haskell, which has abysmal support for structural typing; Haskell is (for various reasons outside the scope of this blog post) aggressively nominal.<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><p>Does this mean Haskell is bad, or that it cannot be practically used to solve these kinds of problems? No, certainly not; there are many ways to model these problems in Haskell that work well enough, though some of them suffer from significant boilerplate. The core thesis of this blog post applies just as much to Haskell as it does to any of the other languages I mentioned above. However, I would be remiss not to mention this distinction, as it may give programmers from a dynamically-typed background who have historically found statically-typed languages much more frustrating to work with a better understanding of the <em>real</em> reason they feel that way. (Essentially all mainstream, statically-typed OOP languages are even more nominal than Haskell!)</p><p>As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is <em>not</em> productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, you could abuse the <code>FromJSON</code> instance to convert an arbitrary string to a <code>UserId</code>, but this would not be as easy as it sounds, since <code>fromJSON</code> can fail. This means you’d somehow have to handle that failure case, so this trick would be unlikely to get you very far unless you’re already in a context where you’re doing input parsing… at which point it would be easier to just do the right thing. So yes, the type system doesn’t prevent you from going out of your way to shoot yourself in the foot, but it guides you towards the right solution (and there is no safeguard in existence that can completely protect a programmer from making their own life miserable if they are determined to do so). <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>I consider this to be Haskell’s most significant flaw at the time of this writing. <a href="#footnote-ref-2-1">↩</a></p></li></ol></article>Parse, don’t validatehttps://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/05 Nov 2019<article><p>Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.</p><p>However, about a month ago, <a href="https://twitter.com/lexi_lambda/status/1182242561655746560">I was reflecting on Twitter</a> about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:</p><div style="text-align: center; font-size: larger"><strong>Parse, don’t validate.</strong></div><h2><a name="the-essence-of-type-driven-design"></a>The essence of type-driven design</h2><p>Alright, I’ll confess: unless you already know what type-driven design is, my catchy slogan probably doesn’t mean all that much to you. Fortunately, that’s what the remainder of this blog post is for. I’m going to explain precisely what I mean in gory detail—but first, we need to practice a little wishful thinking.</p><h3><a name="the-realm-of-possibility"></a>The realm of possibility</h3><p>One of the wonderful things about static type systems is that they can make it possible, and sometimes even easy, to answer questions like “is it possible to write this function?” For an extreme example, consider the following Haskell type signature:</p><pre><code class="pygments"><span class="nf">foo</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Integer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Void</span></code></pre><p>Is it possible to implement <code>foo</code>? Trivially, the answer is <em>no</em>, as <code>Void</code> is a type that contains no values, so it’s impossible for <em>any</em> function to produce a value of type <code>Void</code>.<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> That example is pretty boring, but the question gets much more interesting if we choose a more realistic example:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>This function returns the first element from a list. Is it possible to implement? It certainly doesn’t sound like it does anything very complicated, but if we attempt to implement it, the compiler won’t be satisfied:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><pre><code>warning: [-Wincomplete-patterns] + Pattern match(es) are non-exhaustive + In an equation for ‘head’: Patterns not matched: [] +</code></pre><p>This message is helpfully pointing out that our function is <em>partial</em>, which is to say it is not defined for all possible inputs. Specifically, it is not defined when the input is <code>[]</code>, the empty list. This makes sense, as it isn’t possible to return the first element of a list if the list is empty—there’s no element to return! So, remarkably, we learn this function isn’t possible to implement, either.</p><h3><a name="turning-partial-functions-total"></a>Turning partial functions total</h3><p>To someone coming from a dynamically-typed background, this might seem perplexing. If we have a list, we might very well want to get the first element in it. And indeed, the operation of “getting the first element of a list” isn’t impossible in Haskell, it just requires a little extra ceremony. There are two different ways to fix the <code>head</code> function, and we’ll start with the simplest one.</p><h4><a name="managing-expectations"></a>Managing expectations</h4><p>As established, <code>head</code> is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the <code>Maybe</code> type:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span></code></pre><p>This buys us the freedom we need to implement <code>head</code>—it allows us to return <code>Nothing</code> when we discover we can’t produce a value of type <code>a</code> after all:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">x</span> +<span class="nf">head</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>Problem solved, right? For the moment, yes… but this solution has a hidden cost.</p><p>Returning <code>Maybe</code> is undoubtably convenient when we’re <em>implementing</em> <code>head</code>. However, it becomes significantly less convenient when we want to actually use it! Since <code>head</code> always has the potential to return <code>Nothing</code>, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating. To see why, consider the following code:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">[</span><span class="kt">FilePath</span><span class="p">]</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="n">when</span><span class="w"> </span><span class="p">(</span><span class="n">null</span><span class="w"> </span><span class="n">configDirsList</span><span class="p">)</span><span class="w"> </span><span class="o">$</span> +<span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> +<span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">configDirsList</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">cacheDir</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="n">cacheDir</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="ne">error</span><span class="w"> </span><span class="s">"should never happen; already checked configDirs is non-empty"</span></code></pre><p>When <code>getConfigurationDirectories</code> retrieves a list of file paths from the environment, it proactively checks that the list is non-empty. However, when we use <code>head</code> in <code>main</code> to get the first element of the list, the <code>Maybe FilePath</code> result still requires us to handle a <code>Nothing</code> case that we know will never happen! This is terribly bad for several reasons:</p><ol><li><p>First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter our code with another redundant check?</p></li><li><p>Second, it has a potential performance cost. Although the cost of the redundant check is trivial in this particular example, one could imagine a more complex scenario where the redundant checks could add up, such as if they were happening in a tight loop.</p></li><li><p>Finally, and worst of all, this code is a bug waiting to happen! What if <code>getConfigurationDirectories</code> were modified to stop checking that the list is empty, intentionally or unintentionally? The programmer might not remember to update <code>main</code>, and suddenly the “impossible” error becomes not only possible, but probable.</p></li></ol><p>The need for this redundant check has essentially forced us to punch a hole in our type system. If we could statically <em>prove</em> the <code>Nothing</code> case impossible, then a modification to <code>getConfigurationDirectories</code> that stopped checking if the list was empty would invalidate the proof and trigger a compile-time failure. However, as-written, we’re forced to rely on a test suite or manual inspection to catch the bug.</p><h4><a name="paying-it-forward"></a>Paying it forward</h4><p>Clearly, our modified version of <code>head</code> leaves some things to be desired. Somehow, we’d like it to be smarter: if we already checked that the list was non-empty, <code>head</code> should unconditionally return the first element without forcing us to handle the case we know is impossible. How can we do that?</p><p>Let’s look at the original (partial) type signature for <code>head</code> again:</p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span></code></pre><p>The previous section illustrated that we can turn that partial type signature into a total one by weakening the promise made in the return type. However, since we don’t want to do that, there’s only one thing left that can be changed: the argument type (in this case, <code>[a]</code>). Instead of weakening the return type, we can <em>strengthen</em> the argument type, eliminating the possibility of <code>head</code> ever being called on an empty list in the first place.</p><p>To do this, we need a type that represents non-empty lists. Fortunately, the existing <code>NonEmpty</code> type from <code>Data.List.NonEmpty</code> is exactly that. It has the following definition:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="kt">:|</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span></code></pre><p>Note that <code>NonEmpty a</code> is really just a tuple of an <code>a</code> and an ordinary, possibly-empty <code>[a]</code>. This conveniently models a non-empty list by storing the first element of the list separately from the list’s tail: even if the <code>[a]</code> component is <code>[]</code>, the <code>a</code> component must always be present. This makes <code>head</code> completely trivial to implement:<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup></p><pre><code class="pygments"><span class="nf">head</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">x</span></code></pre><p>Unlike before, GHC accepts this definition without complaint—this definition is <em>total</em>, not partial. We can update our program to use the new implementation:</p><pre><code class="pygments"><span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="kt">FilePath</span><span class="p">)</span> +<span class="nf">getConfigurationDirectories</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirsString</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getEnv</span><span class="w"> </span><span class="s">"CONFIG_DIRS"</span> +<span class="w"> </span><span class="kr">let</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">split</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="n">configDirsString</span> +<span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">nonEmpty</span><span class="w"> </span><span class="n">configDirsList</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="n">nonEmptyConfigDirsList</span> +<span class="w"> </span><span class="kt">Nothing</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"CONFIG_DIRS cannot be empty"</span> + +<span class="nf">main</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">main</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">do</span> +<span class="w"> </span><span class="n">configDirs</span><span class="w"> </span><span class="ow">&lt;-</span><span class="w"> </span><span class="n">getConfigurationDirectories</span> +<span class="w"> </span><span class="n">initializeCache</span><span class="w"> </span><span class="p">(</span><span class="n">head</span><span class="w"> </span><span class="n">configDirs</span><span class="p">)</span></code></pre><p>Note that the redundant check in <code>main</code> is now completely gone! Instead, we perform the check exactly once, in <code>getConfigurationDirectories</code>. It constructs a <code>NonEmpty a</code> from a <code>[a]</code> using the <code>nonEmpty</code> function from <code>Data.List.NonEmpty</code>, which has the following type:</p><pre><code class="pygments"><span class="nf">nonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>The <code>Maybe</code> is still there, but this time, we handle the <code>Nothing</code> case very early in our program: right in the same place we were already doing the input validation. Once that check has passed, we now have a <code>NonEmpty FilePath</code> value, which preserves (in the type system!) the knowledge that the list really is non-empty. Put another way, you can think of a value of type <code>NonEmpty a</code> as being like a value of type <code>[a]</code>, plus a <em>proof</em> that the list is non-empty.</p><p>By strengthening the type of the argument to <code>head</code> instead of weakening the type of its result, we’ve completely eliminated all the problems from the previous section:</p><ul><li><p>The code has no redundant checks, so there can’t be any performance overhead.</p></li><li><p>Furthermore, if <code>getConfigurationDirectories</code> changes to stop checking that the list is non-empty, its return type must change, too. Consequently, <code>main</code> will fail to typecheck, alerting us to the problem before we even run the program!</p></li></ul><p>What’s more, it’s trivial to recover the old behavior of <code>head</code> from the new one by composing <code>head</code> with <code>nonEmpty</code>:</p><pre><code class="pygments"><span class="nf">head&#39;</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span> +<span class="nf">head&#39;</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">fmap</span><span class="w"> </span><span class="n">head</span><span class="w"> </span><span class="o">.</span><span class="w"> </span><span class="n">nonEmpty</span></code></pre><p>Note that the inverse is <em>not</em> true: there is no way to obtain the new version of <code>head</code> from the old one. All in all, the second approach is superior on all axes.</p><h3><a name="the-power-of-parsing"></a>The power of parsing</h3><p>You may be wondering what the above example has to do with the title of this blog post. After all, we only examined two different ways to validate that a list was non-empty—no parsing in sight. That interpretation isn’t wrong, but I’d like to propose another perspective: in my mind, the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:</p><pre><code class="pygments"><span class="nf">validateNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="kr">_</span><span class="kt">:</span><span class="kr">_</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="nb">()</span> +<span class="nf">validateNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span> + +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">[</span><span class="n">a</span><span class="p">]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IO</span><span class="w"> </span><span class="p">(</span><span class="kt">NonEmpty</span><span class="w"> </span><span class="n">a</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:</span><span class="n">xs</span><span class="p">)</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">pure</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="kt">:|</span><span class="n">xs</span><span class="p">)</span> +<span class="nf">parseNonEmpty</span><span class="w"> </span><span class="kt">[]</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="n">throwIO</span><span class="w"> </span><span class="o">$</span><span class="w"> </span><span class="n">userError</span><span class="w"> </span><span class="s">"list cannot be empty"</span></code></pre><p>These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type: <code>validateNonEmpty</code> always returns <code>()</code>, the type that contains no information, but <code>parseNonEmpty</code> returns <code>NonEmpty a</code>, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, but <code>parseNonEmpty</code> gives the caller access to the information it learned, while <code>validateNonEmpty</code> just throws it away.</p><p>These two functions elegantly illustrate two different perspectives on the role of a static type system: <code>validateNonEmpty</code> obeys the typechecker well enough, but only <code>parseNonEmpty</code> takes full advantage of it. If you see why <code>parseNonEmpty</code> is preferable, you understand what I mean by the mantra “parse, don’t validate.” Still, perhaps you are skeptical of <code>parseNonEmpty</code>’s name. Is it really <em>parsing</em> anything, or is it merely validating its input and returning a result? While the precise definition of what it means to parse or validate something is debatable, I believe <code>parseNonEmpty</code> is a bona-fide parser (albeit a particularly simple one).</p><p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. By its very nature, a parser is a partial function—some values in the domain do not correspond to any value in the range—so all parsers must have some notion of failure. Often, the input to a parser is text, but this is by no means a requirement, and <code>parseNonEmpty</code> is a perfectly cromulent parser: it parses lists into non-empty lists, signaling failure by terminating the program with an error message.</p><p>Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again! Haskellers are well-aware of this power, and they use many different types of parsers on a regular basis:</p><ul><li><p>The <a href="https://hackage.haskell.org/package/aeson">aeson</a> library provides a <code>Parser</code> type that can be used to parse JSON data into domain types.</p></li><li><p>Likewise, <a href="https://hackage.haskell.org/package/optparse-applicative">optparse-applicative</a> provides a set of parser combinators for parsing command-line arguments.</p></li><li><p>Database libraries like <a href="https://hackage.haskell.org/package/persistent">persistent</a> and <a href="https://hackage.haskell.org/package/postgresql-simple">postgresql-simple</a> have a mechanism for parsing values held in an external data store.</p></li><li><p>The <a href="https://hackage.haskell.org/package/servant">servant</a> ecosystem is built around parsing Haskell datatypes from path components, query parameters, HTTP headers, and more.</p></li></ul><p>The common theme between all these libraries is that they sit on the boundary between your Haskell application and the external world. That world doesn’t speak in product and sum types, but in streams of bytes, so there’s no getting around a need to do some parsing. Doing that parsing up front, before acting on the data, can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities.</p><p>One drawback to this approach of parsing everything up front is that it sometimes requires values be parsed long before they are actually used. In a dynamically-typed language, this can make keeping the parsing and processing logic in sync a little tricky without extensive test coverage, much of which can be laborious to maintain. However, with a static type system, the problem becomes marvelously simple, as demonstrated by the <code>NonEmpty</code> example above: if the parsing and processing logic go out of sync, the program will fail to even compile.</p><h3><a name="the-danger-of-validation"></a>The danger of validation</h3><p>Hopefully, by this point, you are at least somewhat sold on the idea that parsing is preferable to validation, but you may have lingering doubts. Is validation really so bad if the type system is going to force you to do the necessary checks eventually anyway? Maybe the error reporting will be a little bit worse, but a bit of redundant checking can’t hurt, right?</p><p>Unfortunately, it isn’t so simple. Ad-hoc validation leads to a phenomenon that the <a href="http://langsec.org">language-theoretic security</a> field calls <em>shotgun parsing</em>. In the 2016 paper, <a href="http://langsec.org/papers/langsec-cwes-secdev2016.pdf">The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them</a>, its authors provide the following definition:</p><blockquote><p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code—throwing a cloud of checks at the input, and hoping, without any systematic justification, that one or another would catch all the “bad” cases.</p></blockquote><p>They go on to explain the problems inherent to such validation techniques:</p><blockquote><p>Shotgun parsing necessarily deprives the program of the ability to reject invalid input instead of processing it. Late-discovered errors in an input stream will result in some portion of invalid input having been processed, with the consequence that program state is difficult to accurately predict.</p></blockquote><p>In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible—such as rolling back a transaction in an RDBMS—but in general it may not be.</p><p>It may not be immediately apparent what shotgun parsing has to do with validation—after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called “impossible” cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it’s regularly necessary.</p><p>Parsing avoids this problem by stratifying the program into two phases—parsing and execution—where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p><h2><a name="parsing-not-validating-in-practice"></a>Parsing, not validating, in practice</h2><p>So far, this blog post has been something of a sales pitch. “You, dear reader, ought to be parsing!” it says, and if I’ve done my job properly, at least some of you are sold. However, even if you understand the “what” and the “why,” you might not feel especially confident about the “how.”</p><p>My advice: focus on the datatypes.</p><p>Suppose you are writing a function that accepts a list of tuples representing key-value pairs, and you suddenly realize you aren’t sure what to do if the list has duplicate keys. One solution would be to write a function that asserts there aren’t any duplicates in the list:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="nb">()</span></code></pre><p>However, this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. A better solution is to choose a data structure that disallows duplicate keys by construction, such as a <code>Map</code>. Adjust your function’s type signature to accept a <code>Map</code> instead of a list of tuples, and implement it as you normally would.</p><p>Once you’ve done that, the call site of your new function will likely fail to typecheck, since it is still being passed a list of tuples. If the caller was given the value via one of its arguments, or if it received it from the result of some other function, you can continue updating the type from list to <code>Map</code>, all the way up the call chain. Eventually, you will either reach the location the value is created, or you’ll find a place where duplicates actually ought to be allowed. At that point, you can insert a call to a modified version of <code>checkNoDuplicateKeys</code>:</p><pre><code class="pygments"><span class="nf">checkNoDuplicateKeys</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="p">(</span><span class="kt">MonadError</span><span class="w"> </span><span class="kt">AppError</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="kt">Eq</span><span class="w"> </span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="ow">=&gt;</span><span class="w"> </span><span class="p">[(</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">)]</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="p">(</span><span class="kt">Map</span><span class="w"> </span><span class="n">k</span><span class="w"> </span><span class="n">v</span><span class="p">)</span></code></pre><p>Now the check <em>cannot</em> be omitted, since its result is actually necessary for the program to proceed!</p><p>This hypothetical scenario highlights two simple ideas:</p><ol><li><p><strong>Use a data structure that makes illegal states unrepresentable.</strong> Model your data using the most precise data structure you reasonably can. If ruling out a particular possibility is too hard using the encoding you are currently using, consider alternate encodings that can express the property you care about more easily. Don’t be afraid to refactor.</p></li><li><p><strong>Push the burden of proof upward as far as possible, but no further.</strong> Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p><p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected. Use sum types judiciously to allow your datatypes to reflect and adapt to control flow.</p></li></ol><p>In other words, write functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle. Don’t be afraid to iteratively adjust parts of the design as you go, since you may learn something new during the refactoring process!</p><p>Here are a handful of additional points of advice, arranged in no particular order:</p><ul><li><p><strong>Let your datatypes inform your code, don’t let your code control your datatypes.</strong> Avoid the temptation to just stick a <code>Bool</code> in a record somewhere because it’s needed by the function you’re currently writing. Don’t be afraid to refactor code to use the right data representation—the type system will ensure you’ve covered all the places that need changing, and it will likely save you a headache later.</p></li><li><p><strong>Treat functions that return <code>m ()</code> with deep suspicion.</strong> Sometimes these are genuinely necessary, as they may perform an imperative effect with no meaningful result, but if the primary purpose of that effect is raising an error, it’s likely there’s a better way.</p></li><li><p><strong>Don’t be afraid to parse data in multiple passes.</strong> Avoiding shotgun parsing just means you shouldn’t act on the input data before it’s fully parsed, not that you can’t use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p></li><li><p><strong>Avoid denormalized representations of data, <em>especially</em> if it’s mutable.</strong> Duplicating the same data in multiple places introduces a trivially representable illegal state: the places getting out of sync. Strive for a single source of truth.</p><ul><li><p><strong>Keep denormalized representations of data behind abstraction boundaries.</strong> If denormalization is absolutely necessary, use encapsulation to ensure a small, trusted module holds sole responsibility for keeping the representations in sync.</p></li></ul></li><li><p><strong>Use abstract datatypes to make validators “look like” parsers.</strong> Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract <code>newtype</code> with a smart constructor to “fake” a parser from a validator.</p></li></ul><p>As always, use your best judgement. It probably isn’t worth breaking out <a href="https://hackage.haskell.org/package/singletons">singletons</a> and refactoring your entire application just to get rid of a single <code>error "impossible"</code> call somewhere—just make sure to treat those situations like the radioactive substance they are, and handle them with the appropriate care. If all else fails, at least leave a comment to document the invariant for whoever needs to modify the code next.</p><h2><a name="recap-reflection-and-related-reading"></a>Recap, reflection, and related reading</h2><p>That’s all, really. Hopefully this blog post proves that taking advantage of the Haskell type system doesn’t require a PhD, and it doesn’t even require using the latest and greatest of GHC’s shiny new language extensions—though they can certainly sometimes help! Sometimes the biggest obstacle to using Haskell to its fullest is simply being aware what options are available, and unfortunately, one downside of Haskell’s small community is a relative dearth of resources that document design patterns and techniques that have become tribal knowledge.</p><p>None of the ideas in this blog post are new. In fact, the core idea—“write total functions”—is conceptually quite simple. Despite that, I find it remarkably challenging to communicate actionable, practicable details about the way I write Haskell code. It’s easy to spend lots of time talking about abstract concepts—many of which are quite valuable!—without communicating anything useful about <em>process</em>. My hope is that this is a small step in that direction.</p><p>Sadly, I don’t know very many other resources on this particular topic, but I do know of one: I never hesitate to recommend Matt Parson’s fantastic blog post <a href="https://www.parsonsmatt.org/2017/10/11/type_safety_back_and_forth.html">Type Safety Back and Forth</a>. If you want another accessible perspective on these ideas, including another worked example, I’d highly encourage giving it a read. For a significantly more advanced take on many of these ideas, I can also recommend Matt Noonan’s 2018 paper <a href="https://kataskeue.com/gdp.pdf">Ghosts of Departed Proofs</a>, which outlines a handful of techniques for capturing more complex invariants in the type system than I have described here.</p><p>As a closing note, I want to say that doing the kind of refactoring described in this blog post is not always easy. The examples I’ve given are simple, but real life is often much less straightforward. Even for those experienced in type-driven design, it can be genuinely difficult to capture certain invariants in the type system, so do not consider it a personal failing if you cannot solve something the way you’d like! Consider the principles in this blog post ideals to strive for, not strict requirements to meet. All that matters is to try.</p><ol class="footnotes"><li id="footnote-1"><p>Technically, in Haskell, this ignores “bottoms,” constructions that can inhabit <em>any</em> value. These aren’t “real” values (unlike <code>null</code> in some other languages)—they’re things like infinite loops or computations that raise exceptions—and in idiomatic Haskell, we usually try to avoid them, so reasoning that pretends they don’t exist still has value. But don’t take my word for it—I’ll let Danielsson et al. convince you that <a href="https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf">Fast and Loose Reasoning is Morally Correct</a>. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>In fact, <code>Data.List.NonEmpty</code> already provides a <code>head</code> function with this type, but just for the sake of illustration, we’ll reimplement it ourselves. <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Sometimes it is necessary to perform some kind of authorization before parsing user input to avoid denial of service attacks, but that’s okay: authorization should have a relatively small surface area, and it shouldn’t cause any significant modifications to the state of your system. <a href="#footnote-ref-3-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/typescript.atom.xml b/feeds/typescript.atom.xml new file mode 100644 index 0000000..843a3cf --- /dev/null +++ b/feeds/typescript.atom.xml @@ -0,0 +1,34 @@ +Posts tagged ‘typescript’ | Alexis King’s Blog2020-08-13T00:00:00ZTypes as axioms, or: playing god with static types2020-08-13T00:00:00Z2020-08-13T00:00:00ZAlexis King<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/feeds/typescript.rss.xml b/feeds/typescript.rss.xml new file mode 100644 index 0000000..1d24732 --- /dev/null +++ b/feeds/typescript.rss.xml @@ -0,0 +1,34 @@ +Posts tagged ‘typescript’ | Alexis King’s BlogPosts tagged ‘typescript’ | Alexis King’s Bloghttps://lexi-lambda.github.io/tags/typescript.html13 Aug 202013 Aug 202060Types as axioms, or: playing god with static typeshttps://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/13 Aug 2020<article><p>Just what exactly <em>is</em> a type?</p><p>A common perspective is that types are <em>restrictions</em>. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t <em>really</em> predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.</p><p>But that is not the <em>only</em> perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves <em>you.</em></p><p>…no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.</p><h2><a name="seeing-the-types-half-empty"></a>Seeing the types half-empty</h2><p>Let’s talk a little about TypeScript.</p><p>TypeScript is a <em>gradually-typed</em> language, which means it’s possible to mix statically- and dynamically-typed code. The original intended use case of gradual typing was to <em>gradually</em> add static types to an existing dynamically-typed codebase, which imposes some interesting design constraints. For one, a valid JavaScript program must also be a valid TypeScript program; for another, TypeScript must be accommodating of traditional JavaScript idioms.</p><p>Gradually typed languages like TypeScript are particularly good illustrations of the way type annotations can be viewed as constraints. A function with no explicit type declarations<sup><a href="#footnote-1" id="footnote-ref-1-1">1</a></sup> can accept <em>any</em> JavaScript value, so adding a type annotation fundamentally restricts the set of legal values.</p><p>Furthermore, languages like TypeScript tend to have subtyping. This makes it easy to classify certain types as “more restrictive” than others. For example, a type like <code>string | number</code> clearly includes more values than just <code>number</code>, so <code>number</code> is a more restrictive type—a <em>subtype</em>.</p><p>An exceptionally concrete way to illustrate this “types are restrictions” mentality is to write a function with an unnecessarily specific type. Here’s a TypeScript function that returns the first element in an array of numbers:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">getFirst</span><span class="p">(</span><span class="nx">arr</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="p">[])</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kc">undefined</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">arr</span><span class="p">[</span><span class="mf">0</span><span class="p">];</span> +<span class="p">}</span></code></pre><p>If we ignore the type annotations and consider only the dynamic semantics of JavaScript, this function would work perfectly well given a list of strings. However, if we write <code>getFirst(["hello", "world"])</code>, the typechecker will complain. In this example, the restriction is thoroughly self-imposed—it would be easy to give this function a more generic type—but it’s not always that easy. For example, suppose we wrote a function where the return type depends upon the type of the argument:</p><pre><code class="pygments"><span class="kd">function</span><span class="w"> </span><span class="nx">emptyLike</span><span class="p">(</span><span class="nx">val</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="o">:</span><span class="w"> </span><span class="kt">number</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">val</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"number"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span> +<span class="w"> </span><span class="p">}</span> +<span class="p">}</span></code></pre><p>Now if we write <code>emptyLike(42) * 10</code>, the typechecker will once again complain, claiming the result might be a string—it can’t “figure out” that when we pass a number, we always get a number back.</p><p>When type systems are approached from this perspective, the result is often frustration. The programmer knows that the equivalent untyped JavaScript is perfectly well-behaved, so the typechecker comes off as being the highly unfortunate combination of stubborn yet dim-witted. What’s more, the programmer likely has little mental model of the typechecker’s internal operation, so when types like the above are inferred (not explicitly written), it can be unclear what solutions exist to make the error go away.</p><p>At this point, the programmer may give up. “Stupid typechecker,” they grumble, changing the return type of <code>emptyLike</code> to <code>any</code>. “If it can’t even figure this out, can it <em>really</em> be all that useful?”</p><p>Sadly, this relationship with the typechecker is all too common, and gradually-typed languages in particular tend to create a vicious cycle of frustration:</p><ul><li><p>Gradual type systems are intentionally designed to “just work” on idiomatic code as much as possible, so programmers may not think much about the types except when they get type errors.</p></li><li><p>Furthermore, many programmers using gradually-typed languages are already adept at programming in the underlying dynamically-typed language, so they have working mental models of program operation in terms of the dynamic semantics alone. They are much less likely to develop a rich mental model of the static semantics of the type system because they are used to reasoning without one.</p></li><li><p>Gradually typed languages must support idioms from their dynamically-typed heritage, so they often include ad-hoc special cases (such as, for example, special treatment of <code>typeof</code> checks) that obscure the rules the typechecker follows and make them seem semi-magical.</p></li><li><p>Builtin types are deeply blessed in the type system, strongly encouraging programmers to embrace their full flexibility, but leaving little recourse when they run up against their limits.</p></li><li><p>All this frustration breeds a readiness to override the typechecker using casts or <code>any</code>, which ultimately creates a self-fulfilling prophecy in which the typechecker rarely catches any interesting mistakes because it has been so routinely disabled.</p></li></ul><p>The end result of all of this is a defeatist attitude that views the typechecker as a minor tooling convenience at best (i.e. a fancy autocomplete provider) or an active impediment at worst. Who can really blame them? The type system has (unintentionally of course) been designed in such a way so as to lead them into this dead end. The public perception of type systems settles into that of a strikingly literal nitpicker we endure rather than as a tool we actively leverage.</p><h2><a name="taking-back-types"></a>Taking back types</h2><p>After everything I said above, it may be hard to imagine seeing types any other way. Indeed, through the lens of TypeScript, the “types are restrictions” mentality is incredibly natural, so much so that it seems self-evident. But let’s move away from TypeScript for a moment and focus on a different language, Haskell, which encourages a somewhat different perspective. If you aren’t familiar with Haskell, that’s alright—I’m going to try to keep the examples in this blog post as accessible as possible whether you’ve written any Haskell or not.</p><p>Though Haskell and TypeScript are both statically-typed—and both of their type systems are fairly sophisticated—Haskell’s type system is almost completely different philosophically:</p><ul><li><p>Haskell does not have subtyping,<sup><a href="#footnote-2" id="footnote-ref-2-1">2</a></sup> which means that every value belongs to exactly one type.</p></li><li><p>While JavaScript is built around a small handful of flexible builtin datatypes (booleans, numbers, strings, arrays, and objects), Haskell has essentially no blessed, built-in datatypes other than numbers. Key types such as booleans, lists, and tuples are ordinary datatypes defined in the standard library, no different from types users could define.<sup><a href="#footnote-3" id="footnote-ref-3-1">3</a></sup></p></li><li><p>In particular, Haskell is built around the idea that datatypes can be defined with multiple <em>cases</em>, and branching is done via pattern-matching (more on this shortly).</p></li></ul><p>Let’s look at a basic Haskell datatype declaration. Suppose we want to define a type that represents a season:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Winter</span></code></pre><p>If you are familiar with TypeScript, this may look rather similar to a union type; if you’re familiar with a C-family language, this may remind you more of an enum. Both are on the right track: this defines a new type named <code>Season</code> with four possible values, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>.</p><p>But what exactly <em>are</em> those values?</p><ul><li><p>In TypeScript, we’d represent this type with a union of strings, like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>Here, <code>Season</code> is a type that can be one of those four strings, but nothing else.</p></li><li><p>In C, we’d represent this type with an enum, like this:</p><pre><code class="pygments"><span class="k">enum</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">SPRING</span><span class="p">,</span><span class="w"> </span><span class="n">SUMMER</span><span class="p">,</span><span class="w"> </span><span class="n">FALL</span><span class="p">,</span><span class="w"> </span><span class="n">WINTER</span><span class="w"> </span><span class="p">};</span></code></pre><p>Here, <code>SPRING</code>, <code>SUMMER</code>, <code>FALL</code>, and <code>WINTER</code> are essentially defined to be global aliases for the integers <code>0</code>, <code>1</code>, <code>2</code>, and <code>3</code>, and the type <code>enum season</code> is essentially an alias for <code>int</code>.</p></li></ul><p>So in TypeScript, the values are strings, and in C, the values are numbers. What are they in Haskell? Well… they simply <em>are</em>.</p><p>The Haskell declaration invents four completely new constants out of thin air, <code>Spring</code>, <code>Summer</code>, <code>Fall</code>, and <code>Winter</code>. They aren’t aliases for numbers, nor are they symbols or strings. The compiler doesn’t expose anything about how it chooses to represent these values at runtime; that’s an implementation detail. In Haskell, <code>Spring</code> is now a value <em>distinct from all other values</em>, even if someone in a different module were to also use the name <code>Spring</code>. Haskell type declarations let us play god, creating something from nothing.</p><p>Since these values are totally unique, abstract constants, what can we actually do with them? The answer is one thing and <em>exactly</em> one thing: we can branch on them. For example, we can write a function that takes a <code>Season</code> as an argument and returns whether or not Christmas occurs during it:</p><pre><code class="pygments"><span class="nf">containsChristmas</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Bool</span> +<span class="nf">containsChristmas</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- southern hemisphere</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">False</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">True</span><span class="w"> </span><span class="c1">-- northern hemisphere</span></code></pre><p><code>case</code> expressions are, to a first approximation, a lot like C-style <code>switch</code> statements (though they can do a lot more than this simple example suggests). Using <code>case</code>, we can also define conversions from our totally unique <code>Season</code> constants to other types, if we want:</p><pre><code class="pygments"><span class="nf">seasonToString</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">String</span> +<span class="nf">seasonToString</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">season</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="kt">Spring</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"spring"</span> +<span class="w"> </span><span class="kt">Summer</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"summer"</span> +<span class="w"> </span><span class="kt">Fall</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"fall"</span> +<span class="w"> </span><span class="kt">Winter</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="s">"winter"</span></code></pre><p>We can also go the other way around, converting a <code>String</code> to a <code>Season</code>, but if we try, we run into a problem: what do we return for a string like, say, <code>"cheesecake"</code>? In other languages, we might throw an error or return <code>null</code>, but Haskell does not have <code>null</code>, and errors are generally reserved for truly catastrophic failures. What can we do instead?</p><p>A particularly naïve solution would be to create a type called <code>MaybeASeason</code> that has two cases—it can be a valid <code>Season</code>, or it can be <code>NotASeason</code>:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">MaybeASeason</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">NotASeason</span> + +<span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">MaybeASeason</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">IsASeason</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">NotASeason</span></code></pre><p>This shows a feature of Haskell datatypes that C-style enums do <em>not</em> have: they aren’t just constants, they can contain other values. A <code>MaybeASeason</code> can be one of five different values: <code>IsASeason Spring</code>, <code>IsASeason Summer</code>, <code>IsASeason Fall</code>, <code>IsASeason Winter</code>, or <code>NotASeason</code>.</p><p>In TypeScript, we’d write <code>MaybeASeason</code> more like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">MaybeASeason</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"not-a-season"</span><span class="p">;</span></code></pre><p>This is kind of nice, because we don’t have to wrap all our <code>Season</code> values with <code>IsASeason</code> like we have to do in Haskell. But remember that Haskell doesn’t have subtyping—every value must belong to exactly one type—so the Haskell code needs the <code>IsASeason</code> wrapper to distinguish the value as a <code>MaybeASeason</code> rather than a <code>Season</code>.</p><p>Now, you may rightly point out that having to invent a type like <code>MaybeASeason</code> every time we need to create a variant of a type with a failure case is absurd, so fortunately we can define a type like <code>MaybeASeason</code> that works for <em>any</em> underlying type. In Haskell, it looks like this:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p>This defines a generic type, where the <code>a</code> in <code>Maybe a</code> is a stand-in for some other type, much like the <code>T</code> in <code>Array&lt;T&gt;</code> in other languages. We can change our <code>stringToSeason</code> function to use <code>Maybe</code>:</p><pre><code class="pygments"><span class="nf">stringToSeason</span><span class="w"> </span><span class="ow">::</span><span class="w"> </span><span class="kt">String</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Maybe</span><span class="w"> </span><span class="kt">Season</span> +<span class="nf">stringToSeason</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kr">case</span><span class="w"> </span><span class="n">seasonString</span><span class="w"> </span><span class="kr">of</span> +<span class="w"> </span><span class="s">"spring"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Spring</span> +<span class="w"> </span><span class="s">"summer"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Summer</span> +<span class="w"> </span><span class="s">"fall"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Fall</span> +<span class="w"> </span><span class="s">"winter"</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Just</span><span class="w"> </span><span class="kt">Winter</span> +<span class="w"> </span><span class="kr">_</span><span class="w"> </span><span class="ow">-&gt;</span><span class="w"> </span><span class="kt">Nothing</span></code></pre><p><code>Maybe</code> gets us something a lot like nullable types, but it isn’t built into the type system, it’s just an ordinary type defined in the standard library.</p><h3><a name="positive-versus-negative-space"></a>Positive versus negative space</h3><p>At this point, you may be wondering to yourself why I am talking about all of this, seeing as everything in the previous section is information you could find in a basic Haskell tutorial. But the point of this blog post is not to teach you Haskell, it’s to focus on a particular philosophical approach to modeling data.</p><p>In TypeScript, when we write a type declaration like</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">Season</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"summer"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"spring"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"fall"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="s2">"winter"</span><span class="p">;</span></code></pre><p>we are defining a type that can be one of those four strings <em>and nothing else</em>. All the other strings that <em>aren’t</em> one of those four make up <code>Season</code>’s “negative space”—values that exist, but that we have intentionally excluded. In contrast, the Haskell type does not really have any “negative space” because we pulled four new values out of thin air.</p><p>Of course, I suspect you don’t really buy this argument. What makes a string like <code>"cheesecake"</code> “negative space” in TypeScript but not in Haskell? Well… nothing, really. The distinction I’m drawing here doesn’t really exist, it’s just a different perspective, and arguably a totally contrived and arbitrary one. But now that I’ve explained the premise and set up some context, let me provide a more compelling example.</p><p>Suppose you are writing a TypeScript program, and you want a function that only accepts <em>non-empty</em> arrays. What can you do? Your first instinct is that you need a way to somehow further restrict the function’s input type to exclude empty arrays. And indeed, there <em>is</em> a trick for doing that:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">NonEmptyArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="p">...</span><span class="nx">T</span><span class="p">[]];</span></code></pre><p>Great! But what if the constraint was more complicated: what if you needed an array containing an even number of elements? Unfortunately, there isn’t really a trick for that one. At this point, you might start wishing the type system had support for something really fancy, like refinement types, so you could write something like this:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenArray</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="nx">satisfies</span><span class="w"> </span><span class="p">(</span><span class="nx">arr</span><span class="w"> </span><span class="p">=&gt;</span><span class="w"> </span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">);</span></code></pre><p>But TypeScript doesn’t support anything like that, so for now you’re stuck. You need a way to restrict the function’s domain in a way the type system does not have any special support for, so your conclusion might be “I guess the type system just can’t do this.” People tend to call this “running up against the limits of the type system.”</p><p>But what if we took a different perspective? Recall that in Haskell, lists aren’t built-in datatypes, they’re just ordinary datatypes defined in the standard library:<sup><a href="#footnote-4" id="footnote-ref-4-1">4</a></sup></p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>This type might be a bit confusing at first if you have not written any Haskell, since it’s <em>recursive</em>. All of these are valid values of type <code>List Int</code>:</p><ul><li><p><code>Nil</code></p></li><li><p><code>Cons 1 Nil</code></p></li><li><p><code>Cons 1 (Cons 2 Nil)</code></p></li><li><p><code>Cons 1 (Cons 2 (Cons 3 Nil))</code></p></li></ul><p>The recursive nature of <code>Cons</code> is what gives our user-defined datatype the ability to hold any number of values: we can have any number of nested <code>Cons</code>es we want before we terminate the list with a final <code>Nil</code>.</p><p>If we wanted to define an <code>EvenList</code> type in Haskell, we might end up thinking along the same lines we did before, that we need some fancy type system extension so we can restrict <code>List</code> to exclude lists with odd numbers of elements. But that’s focusing on the negative space of things we want to exclude… what if instead, we focused on the <em>positive</em> space of things we want to <em>include?</em></p><p>What do I mean by that? Well, we could define an entirely new type that’s just like <code>List</code>, but we make it <em>impossible</em> to ever include an odd number of elements:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Here are some valid values of type <code>EvenList Int</code>:</p><ul><li><p><code>EvenNil</code></p></li><li><p><code>EvenCons 1 2 EvenNil</code></p></li><li><p><code>EvenCons 1 2 (EvenCons 3 4 EvenNil)</code></p></li></ul><p>Lo and behold, a datatype that can only ever include even numbers of elements!</p><p>Now, at this point you might realize that this is kind of silly. We don’t need to invent an entirely new datatype for this! We could just create a list of pairs:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Now values like <code>Cons (1, 2) (Cons (3, 4) Nil)</code> would be valid values of type <code>EvenList Int</code>, and we wouldn’t have to reinvent lists. But again, this is an approach based on thinking not on which values we want to exclude, but rather how to structure our data such that those illegal values aren’t even <em>constructible.</em></p><p><strong>This is the essence of the Haskeller’s mantra, “Make illegal states unrepresentable,”</strong> and sadly it is often misinterpreted. It’s much easier to think “hm, I want to make these states illegal, how can I add some post-hoc restrictions to rule them out?” And indeed, this is why refinement types really <em>are</em> awesome, and when they’re available, by all means use them! But checking totally arbitrary properties at the type level is not tractable in general, and sometimes you need to think a little more outside the box.</p><h3><a name="types-as-axiom-schemas"></a>Types as axiom schemas</h3><p>So far in this blog post, I’ve repeatedly touched upon a handful of different ideas in a few different ways:</p><ul><li><p>Instead of thinking about how to <em>restrict</em>, it can be useful to think about how to <em>correctly construct</em>.</p></li><li><p>In Haskell, datatype declarations invent new values out of thin air.</p></li><li><p>We can represent a <em>lot</em> of different data structures using the incredibly simple framework of “datatypes with several possibilities.”</p></li></ul><p>Independently, those ideas might not seem deeply related, but in fact, they’re all essential to the Haskell school of data modeling. I want to now explore how we can unify them into a single framework that makes this seem less magical and more like an iterative design process.</p><p>In Haskell, when you define a datatype, you’re really defining a new, self-contained set of <em>axioms</em> and <em>inference rules.</em> That is rather abstract, so let’s make it more concrete. Consider the <code>List</code> type again:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Nil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Cons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Viewed as an axiom schema, this type has one axiom and one inference rule:</p><ul><li><p>The empty list is a list.</p></li><li><p>If you have a list, and you add an element to the beginning, the result is also a list.</p></li></ul><p>The axiom is <code>Nil</code>, and the inference rule is <code>Cons</code>. Every list<sup><a href="#footnote-5" id="footnote-ref-5-1">5</a></sup> is constructed by starting with the axiom, <code>Nil</code>, followed by some number of applications of the inference rule, <code>Cons</code>.</p><p>We can take a similar approach when designing the <code>EvenList</code> type. The axiom is the same:</p><ul><li><p>The empty list is a list with an even number of elements.</p></li></ul><p>But our inference rule must preserve the invariant that the list always contains an even number of elements. We can do this by always adding two elements at a time:</p><ul><li><p>If you have a list with an even number of elements, and you add two elements to the beginning, the result is also a list with an even number of elements.</p></li></ul><p>This corresponds precisely to our <code>EvenList</code> declaration:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">EvenNil</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">EvenCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">EvenList</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>We can also go through this same reasoning process to come up with a type that represents non-empty lists. That type has just one inference rule:</p><ul><li><p>If you have a list, and you add an element to the beginning, the result is a non-empty list.</p></li></ul><p>That inference rule corresponds to the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">NonEmptyList</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">NonEmptyCons</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="p">(</span><span class="kt">List</span><span class="w"> </span><span class="n">a</span><span class="p">)</span></code></pre><p>Of course, it’s possible to do this with much more than just lists. A particularly classic example is the constructive definition of natural numbers:</p><ul><li><p>Zero is a natural number.</p></li><li><p>If you have a natural number, its successor (i.e. that number plus one) is also a natural number.</p></li></ul><p>These are two of the <a href="https://en.wikipedia.org/wiki/Peano_axioms">Peano axioms</a>, which can be represented in Haskell as the following datatype:</p><pre><code class="pygments"><span class="kr">data</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="ow">=</span><span class="w"> </span><span class="kt">Zero</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kt">Succ</span><span class="w"> </span><span class="kt">Natural</span></code></pre><p>Using this type, <code>Zero</code> represents 0, <code>Succ Zero</code> represents 1, <code>Succ (Succ Zero)</code> represents 2, and so on. Just as <code>EvenList</code> allowed us to represent any list with an even number of elements but made other values impossible to even express, this <code>Natural</code> type allows us to represent all natural numbers, while other numbers (such as, for example, negative integers) are impossible to express.</p><p>Now, of course, all this hinges on our interpretation of the values we’ve invented! We have chosen to interpret <code>Zero</code> as <code>0</code> and <code>Succ n</code> as <code>n + 1</code>, but that interpretation is not inherent to <code>Natural</code>’s definition—it’s all in our heads! We could choose to interpret <code>Succ n</code> as <code>n - 1</code> instead, in which case we would only be able to represent non-positive integers, or we could interpret <code>Zero</code> as <code>1</code> and <code>Succ n</code> as <code>n * 2</code>, in which case we could only represent powers of two.</p><p>I find that people sometimes find this approach troubling, or at least counterintuitive. Is <code>Succ (Succ Zero)</code> <em>really</em> 2? It certainly doesn’t look like a number we’re used to writing. When someone thinks “I need a datatype for a number greater than or equal to zero,” they’re going to reach for the type in their programming language called <code>number</code> or <code>int</code>, not think to invent a recursive datatype. And admittedly, the <code>Natural</code> type defined here is not very practical: it’s an incredibly inefficient representation of natural numbers.</p><p>But in less contrived situations, this approach <em>is</em> practical, and in fact it’s highly useful! The quibble that an <code>EvenList Int</code> isn’t “really” a <code>List Int</code> is rather meaningless, seeing as our definition of <code>List</code> was just as arbitrary. A great deal of our jobs as programmers is imbuing arbitrary symbols with meaning; at some point someone decided that the number 65 would correspond to the capital letter A, and it was no less arbitrary then.</p><p>So when you have a property you want to capture in your types, take a step back and think about it for a little bit. Is there a way you can structure your data so that, no matter how you build it, the result is always a valid value? In other words, don’t try to add post-hoc restrictions to exclude bad values, <strong>make your datatypes correct by construction</strong>.</p><h2><a name="but-what-if-i-don-t-write-haskell-and-other-closing-thoughts"></a>“But what if I don’t write Haskell?” And other closing thoughts</h2><p>I write Haskell for a living, and I wrote this blog post with both my coworkers and the broader Haskell community in mind, but if I had <em>only</em> written it with those people in mind, it wouldn’t make sense to have spent so much time explaining basic Haskell. These techniques can be used in almost any statically typed programming language, though it’s certainly easier in some than others.</p><p>I don’t want people to come away from this blog post with an impression that I think TypeScript is a bad language, or that I’m claiming Haskell can do things TypeScript can’t. In fact, TypeScript <em>can</em> do all the things I’ve talked about in this blog post! As proof, here are TypeScript definitions of both <code>EvenList</code> and <code>Natural</code>:</p><pre><code class="pygments"><span class="kr">type</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">[</span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">T</span><span class="p">,</span><span class="w"> </span><span class="nx">EvenList</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">];</span> +<span class="kr">type</span><span class="w"> </span><span class="nx">Natural</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"zero"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">succ</span><span class="o">:</span><span class="w"> </span><span class="kt">Natural</span><span class="w"> </span><span class="p">};</span></code></pre><p>If anything, <strong>the real point of this blog post is that a type system does not have a well-defined list of things it “can prove” and “can’t prove.”</strong> Languages like TypeScript don’t really encourage this approach to data modeling, where you restructure your values in a certain way so as to guarantee certain properties. Rather, they prefer to add increasingly sophisticated constraints and type system features that can capture the properties people want to capture without having to change their data representation.</p><p>And in general, <em>that’s great!</em></p><p>Being able to reuse the same data representation is <em>hugely</em> beneficial. Functions like <code>map</code> and <code>filter</code> already exist for ordinary lists/arrays, but a home-grown <code>EvenList</code> type needs its own versions. Passing an <code>EvenList</code> to a function that expects a list requires explicitly converting between the two. All these things have both code complexity and performance costs, and type system features that make these issues just invisibly disappear are <em>obviously</em> a good thing.</p><p>But the danger of treating the type system this way is that it means you may find yourself unsure what to do when suddenly you have a new requirement that the type system doesn’t provide built-in support for. What then? Do you start punching holes through your type system? The more you do that, the less useful the type system becomes: type systems are great at detecting how changes in one part of a codebase can impact seemingly-unrelated areas in surprising ways, but every unsafe cast or use of <code>any</code> is a hard stop, a point past which the typechecker cannot propagate information. Do that once or twice in a leaf function, it’s okay, but do that even just a half dozen times in your application’s connective tissue, and your type system might not be able to catch those things anymore.</p><p>Even if it isn’t a technique you use every day, it’s worth getting comfortable tweaking your data representation to preserve those guarantees. It’s a magical experience having the typechecker teach you things about your domain you hadn’t even considered simply because you got a type error and started thinking through why. Yes, it’s extra work, but trust me: it’s a lot more pleasant to work for your typechecker when you know exactly how much your typechecker is working for you.</p><ol class="footnotes"><li id="footnote-1"><p>Sort of. TypeScript will try to infer type annotations based on how variables and functions are used, but by default, it falls back on the dynamic, unchecked <code>any</code> type if it can’t find a solution that makes the program typecheck. That behavior can be changed via a configuration option, but that isn’t relevant here: I’m just trying to illustrate a perspective, not make any kind of value judgment about TypeScript specifically. <a href="#footnote-ref-1-1">↩</a></p></li><li id="footnote-2"><p>Sort of. Haskell does have a limited notion of subtyping when polymorphism is involved; for example, the type <code>forall a. a -&gt; a</code> is a subtype of the type <code>Int -&gt; Int</code>. But Haskell does not have anything resembling inheritance (e.g. there is no common <code>Number</code> supertype that includes both <code>Int</code> and <code>Double</code>) nor does it have untagged unions (e.g. the argument to a function cannot be something like <code>Int | String</code>, you must define a wrapper type like <code>data IntOrString = AnInt Int | AString String</code>). <a href="#footnote-ref-2-1">↩</a></p></li><li id="footnote-3"><p>Lists, tuples, and strings do technically have special <em>syntax</em>, which is built into the compiler, but there is truly nothing special about their semantics. They would work exactly the same way without the syntax, the code would just look less pretty. <a href="#footnote-ref-3-1">↩</a></p></li><li id="footnote-4"><p>Haskell programmers will notice that this is not actually the definition of the list type, since the real list type uses special syntax, but I wanted to keep things as simple as possible for this blog post. <a href="#footnote-ref-4-1">↩</a></p></li><li id="footnote-5"><p>Ignoring infinite lists, but the fact that infinite lists are representable in Haskell is outside the scope of this blog post. <a href="#footnote-ref-5-1">↩</a></p></li></ol></article> \ No newline at end of file diff --git a/gulpfile.coffee b/gulpfile.coffee deleted file mode 100644 index 5586c67..0000000 --- a/gulpfile.coffee +++ /dev/null @@ -1,36 +0,0 @@ -import gulp from 'gulp' - -import autoprefixer from 'gulp-autoprefixer' -import compileCoffee from 'gulp-coffee' -import concat from 'gulp-concat' -import rename from 'gulp-rename' -import compileSass from 'gulp-sass' -import uglify from 'gulp-uglify' - -export js = -> - gulp.src 'coffee/**/*.coffee', sourcemaps: true - .pipe compileCoffee() - .pipe concat 'application.js' - .pipe uglify() - .pipe rename extname: '.min.js' - .pipe gulp.dest 'output/js/' - -export css = -> - gulp.src 'scss/**/*.scss', sourcemaps: true - .pipe compileSass().on 'error', compileSass.logError - .pipe autoprefixer() - .pipe rename extname: '.min.css' - .pipe gulp.dest 'output/css/' - -export images = -> - gulp.src 'images/**/*' - .pipe gulp.dest 'output/img/' - -export build = gulp.parallel js, css, images - -export watch = -> - gulp.watch 'coffee/**/*.coffee', js - gulp.watch 'scss/**/*.scss', css - gulp.watch 'images/**/*', images - -export default build diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 8b255e0..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,13 +0,0 @@ -require('coffeescript/register'); -// Patch coffeescript/register to enable transpilation -const coffee = require.extensions['.coffee']; -require.extensions['.coffee'] = (module, filename) => { - if (!module.options) { - module.options = {}; - } - if (!module.options.transpile) { - module.options.transpile = { filename }; - } - return coffee(module, filename); -} -module.exports = require('./gulpfile.coffee'); diff --git a/images/hackett-pict-red-blue-squares.png b/img/hackett-pict-red-blue-squares.png similarity index 100% rename from images/hackett-pict-red-blue-squares.png rename to img/hackett-pict-red-blue-squares.png diff --git a/images/hackett-pict-red-square.png b/img/hackett-pict-red-square.png similarity index 100% rename from images/hackett-pict-red-square.png rename to img/hackett-pict-red-square.png diff --git a/images/hackett-snake-animation.gif b/img/hackett-snake-animation.gif similarity index 100% rename from images/hackett-snake-animation.gif rename to img/hackett-snake-animation.gif diff --git a/images/scribble-docs-hackett-functor.png b/img/scribble-docs-hackett-functor.png similarity index 100% rename from images/scribble-docs-hackett-functor.png rename to img/scribble-docs-hackett-functor.png diff --git a/images/scribble-docs-racket-add1.png b/img/scribble-docs-racket-add1.png similarity index 100% rename from images/scribble-docs-racket-add1.png rename to img/scribble-docs-racket-add1.png diff --git a/index-2.html b/index-2.html new file mode 100644 index 0000000..73dba7e --- /dev/null +++ b/index-2.html @@ -0,0 +1 @@ +Alexis King’s Blog

                                            Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions

                                            ⦿ racket, macros

                                            In my previous blog post, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.

                                            Read more

                                            Reimplementing Hackett’s type language: expanding to custom core forms in Racket

                                            ⦿ racket, hackett, macros

                                            In the past couple of weeks, I completely rewrote the implementation of Hackett’s type language to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.

                                            This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like my previous blog post on Hackett, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.

                                            Read more

                                            An opinionated guide to Haskell in 2018

                                            ⦿ haskell

                                            For me, this month marks the end of an era in my life: as of February 2018, I am no longer employed writing Haskell. It’s been a fascinating two years, and while I am excitedly looking forward to what I’ll be doing next, it’s likely I will continue to write Haskell in my spare time. I’ll probably even write it again professionally in the future.

                                            In the meantime, in the interest of both sharing with others the small amount of wisdom I’ve gained and preserving it for my future self, I’ve decided to write a long, rather dry overview of a few select parts of the Haskell workflow I developed and the ecosystem I settled into. This guide is, as the title notes, opinionated—it is what I used in my day-to-day work, nothing more—and I don’t claim that anything here is the only way to write Haskell, nor even the best way. It is merely what I found helpful and productive. Take from it as much or as little as you’d like.

                                            Read more

                                            A space of their own: adding a type namespace to Hackett

                                            As previously discussed on this blog, my programming language, Hackett, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?

                                            For now, at least, the answer is that Hackett will emulate Haskell: Hackett now has two namespaces. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.

                                            Read more

                                            Hackett progress report: documentation, quality of life, and snake

                                            ⦿ hackett, racket, haskell

                                            Three months ago, I wrote a blog post describing my new, prototype implementation of my programming language, Hackett. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce the first (albeit quite incomplete) approach to Hackett’s documentation.

                                            I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.

                                            Read more

                                            User-programmable infix operators in Racket

                                            ⦿ racket, hackett, macros

                                            Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in Hackett, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.

                                            Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done without modifying the stock #lang racket reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.

                                            Read more

                                            Unit testing effectful Haskell with monad-mock

                                            ⦿ haskell, testing

                                            Nearly eight months ago, I wrote a blog post about unit testing effectful Haskell code using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, monad-mock.

                                            Read more

                                            Realizing Hackett, a metaprogrammable Haskell

                                            Almost five months ago, I wrote a blog post about my new programming language, Hackett, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.

                                            Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, Hackett is not only real, it’s working, and you can try it out yourself!

                                            Read more

                                            Lifts for free: making mtl typeclasses derivable

                                            ⦿ haskell

                                            Perhaps the most important abstraction a Haskell programmer must understand to effectively write modern Haskell code, beyond the level of the monad, is the monad transformer, a way to compose monads together in a limited fashion. One frustrating downside to monad transformers is a proliferation of lifts, which explicitly indicate which monad in a transformer “stack” a particular computation should run in. Fortunately, the venerable mtl provides typeclasses that make this lifting mostly automatic, using typeclass machinery to insert lift where appropriate.

                                            Less fortunately, the mtl approach does not actually eliminate lift entirely, it simply moves it from use sites to instances. This requires a small zoo of extraordinarily boilerplate-y instances, most of which simply implement each typeclass method using lift. While we cannot eliminate the instances entirely without somewhat dangerous techniques like overlapping instances, we can automatically derive them using features of modern GHC, eliminating the truly unnecessary boilerplate.

                                            Read more

                                            Rascal is now Hackett, plus some answers to questions

                                            Since I published my blog post introducing Rascal, I’ve gotten some amazing feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that Rascal is a language that already exists. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, Rascal is now known as Hackett.

                                            With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.

                                            Read more

                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/index-3.html b/index-3.html new file mode 100644 index 0000000..13f8347 --- /dev/null +++ b/index-3.html @@ -0,0 +1 @@ +Alexis King’s Blog

                                            Using types to unit-test in Haskell

                                            ⦿ haskell, testing

                                            Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.

                                            Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.

                                            Read more

                                            Understanding the npm dependency model

                                            ⦿ javascript

                                            Currently, npm is the package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like Bower are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects you as a user or package developer.

                                            Read more

                                            Climbing the infinite ladder of abstraction

                                            I started programming in elementary school.

                                            When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to solve the general problem. When I learned about programming, I was immediately hooked: it was so easy to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.

                                            Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of abstraction.

                                            Read more

                                            Four months with Haskell

                                            At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.

                                            Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an incredible programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.

                                            Read more

                                            Simple, safe multimethods in Racket

                                            ⦿ racket, macros

                                            Racket ships with racket/generic, a system for defining generic methods, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports single dispatch. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.

                                            Read more

                                            ADTs in Typed Racket with macros

                                            ⦿ racket, typed racket, macros

                                            Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate why macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is ADTs.

                                            Read more

                                            Functionally updating record types in Elm

                                            ⦿ elm

                                            Elm is a wonderful language for building web apps, and I love so much of its approach to language design. Elm does so many things right straight out of the box, and that's a real breath of fresh air in the intersection of functional programming and web development. Still, it gets one thing wrong, and unfortunately, that one thing is incredibly important. Elm took the "functions" out of "functional record types".

                                            Almost any software program, at its core, is all about data. Maybe it's about computing data, maybe it's about manipulating data, or maybe it's about displaying data, but at the end of the day, some sort of data model is going to be needed. The functional model is a breathtakingly elegant system for handling data and shuttling it around throughout a program, and functional reactive programming, which Elm uses to model event-like interactions, makes this model work even better. The really important thing, though, is what tools Elm actually gives you to model your data.

                                            Read more

                                            Canonical factories for testing with factory_girl_api

                                            ⦿ ruby, rails, javascript, angular

                                            Modern web applications are often built as single-page apps, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.

                                            To attempt to address a fraction of this problem, I built factory_girl_api, a way to share context setup between both sides of the application.

                                            Read more

                                            Managing application configuration with Envy

                                            ⦿ envy, racket, 12factor

                                            Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, The Twelve-Factor App provides a set of standards for keeping web apps sane, and one of those guidelines advises keeping configuration in the environment.

                                            Envy is the declarative bridge between Racket code and the outside world of the environment.

                                            Read more

                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/index-4.html b/index-4.html new file mode 100644 index 0000000..7e6cd5e --- /dev/null +++ b/index-4.html @@ -0,0 +1 @@ +Alexis King’s Blog

                                            Deploying Racket applications on Heroku

                                            ⦿ racket, heroku, 12factor

                                            Heroku is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.

                                            Read more

                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..e5a0976 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ +Alexis King’s Blog

                                            A break from programming languages

                                            This is a blog post I have been considering writing for a long time.

                                            People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.

                                            My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.

                                            This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for me, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why not—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.

                                            Read more

                                            An introduction to typeclass metaprogramming

                                            Typeclass metaprogramming is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the servant ecosystem), and it is the core mechanism used to implement generic programming via GHC generics. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.

                                            This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does not attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also not a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.

                                            Read more

                                            Names are not type safety

                                            Haskell programmers spend a lot of time talking about type safety. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published Parse, Don’t Validate as an initial stab towards bridging that gap.

                                            The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s newtype construct. The idea is simple enough—the newtype keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this sounds like a simple and straightforward path to type safety. For example, one might consider using a newtype declaration to define a type for an email address:

                                            newtype EmailAddress = EmailAddress Text

                                            This technique can provide some value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct kind of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.

                                            And names are not type safety.

                                            Read more

                                            Types as axioms, or: playing god with static types

                                            Just what exactly is a type?

                                            A common perspective is that types are restrictions. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t really predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.

                                            But that is not the only perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves you.

                                            …no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.

                                            Read more

                                            No, dynamic type systems are not inherently more open

                                            Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.

                                            This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are not about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.

                                            Read more

                                            Parse, don’t validate

                                            Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.

                                            However, about a month ago, I was reflecting on Twitter about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:

                                            Parse, don’t validate.

                                            Read more

                                            Empathy and subjective experience in programming languages

                                            A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.

                                            Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?

                                            I think about that question a lot.

                                            Read more

                                            Demystifying MonadBaseControl

                                            ⦿ haskell

                                            MonadBaseControl from the monad-control package is a confusing typeclass, and its methods have complicated types. For many people, it’s nothing more than scary, impossible-to-understand magic that is, for some reason, needed when lifting certain kinds of operations. Few resources exist that adequately explain how, why, and when it works, which sadly seems to have resulted in some FUD about its use.

                                            There’s no doubt that the machinery of MonadBaseControl is complex, and the role it plays in practice is often subtle. However, its essence is actually much simpler than it appears, and I promise it can be understood by mere mortals. In this blog post, I hope to provide a complete survey of MonadBaseControl—how it works, how it’s designed, and how it can go wrong—in a way that is accessible to anyone with a firm grasp of monads and monad transformers. To start, we’ll motivate MonadBaseControl by reinventing it ourselves.

                                            Read more

                                            Defeating Racket’s separate compilation guarantee

                                            ⦿ racket, macros

                                            Being a self-described programming-language programming language is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate composable and compilable macros. One of the module system’s foundational properties is its separate compilation guarantee, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is essential for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.

                                            Yet today, in this blog post, we’re going to do exactly that.

                                            Read more

                                            Macroexpand anywhere with local-apply-transformer!

                                            ⦿ racket, macros

                                            Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only certain positions in Racket code are subject to macroexpansion.

                                            To illustrate, consider a macro that provides a Clojure-style let form:

                                            (require syntax/parse/define)
                                            +
                                            +(define-simple-macro (clj-let [{~seq x:id e:expr} ...] body:expr ...+)
                                            +  (let ([x e] ...) body ...))

                                            This can be used anywhere an expression is expected, and it does as one would expect:

                                            > (clj-let [x 1
                                            +            y 2]
                                            +    (+ x y))
                                            +3

                                            However, a novice macro programmer might realize that clj-let really only modifies the syntax of binding pairs for a let form. Therefore, could one define a macro that only adjusts the binding pairs of some existing let form instead of expanding to an entire let? That is, could one write the above example like this:

                                            (define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...])
                                            +  ([x e] ...))
                                            +
                                            +> (let (clj-binding-pairs
                                            +        [x 1
                                            +         y 2])
                                            +    (+ x y))
                                            +3

                                            The answer is no: the binding pairs of a let form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion anywhere in a Racket program.

                                            Read more

                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/package.json b/package.json deleted file mode 100644 index c532b1a..0000000 --- a/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "scripts": { - "build": "gulp", - "watch": "gulp watch" - }, - "devDependencies": { - "@babel/cli": "^7.4.3", - "@babel/core": "^7.4.3", - "@babel/preset-env": "^7.4.3", - "coffeescript": "^2.4.1", - "gulp": "^4.0.0", - "gulp-autoprefixer": "^6.1.0", - "gulp-babel": "^8.0.0", - "gulp-coffee": "^3.0.3", - "gulp-concat": "^2.6.1", - "gulp-rename": "^1.4.0", - "gulp-sass": "^4.0.2", - "gulp-uglify": "^3.0.0" - }, - "babel": { - "presets": [ - "@babel/preset-env" - ] - } -} diff --git a/scss/_code.scss b/scss/_code.scss deleted file mode 100644 index be0f9c4..0000000 --- a/scss/_code.scss +++ /dev/null @@ -1,30 +0,0 @@ -@import 'variables'; - -pre, code { - font-family: $font-monospace; - font-variant-ligatures: none; -} - -p code, li code, table code { - background-color: $color-section-background; - border-radius: 3px; - padding: 2px 5px; -} - -pre code { - background-color: $color-section-background; - border-radius: 10px; - display: block; - font-size: 0.9em; - line-height: 1.75em; - margin: 1em auto; - overflow: auto; - padding: 20px; - - // enable inertial horizontal scrolling - -webkit-overflow-scrolling: touch; - - @media (min-width: 800px) { - margin: 2em auto; - } -} diff --git a/scss/_variables.scss b/scss/_variables.scss deleted file mode 100644 index 0205958..0000000 --- a/scss/_variables.scss +++ /dev/null @@ -1,24 +0,0 @@ -$font-serif: 'Merriweather', serif; -$font-sans-serif: 'Merriweather Sans', sans-serif; -$font-monospace: 'Fira Code', monospace; - -$color-background: #fffcfc; -$color-primary: #e45b5b; -$color-disabled: #ac9c9c; -$color-section-background: #fffafa; -$color-section-background-border: #fef8f8; -$color-section-background-highlight: #fff5f5; -$color-table-border: #fff1f1; - -$text-color-light: lighten($color-primary, 30%); -$text-color-link: desaturate(darken($color-primary, 10%), 20%); -$text-color-link-light: lighten($text-color-link, 10%); -$text-color-primary: #312323; - -$content-inset-small: 40px; -$content-inset-large: 75px; - -$content-width-small: 700px; -$content-width-medium: 770px; -$content-width-large: 850px; -$content-width-max: 1000px; diff --git a/scss/application.scss b/scss/application.scss deleted file mode 100644 index a30a36b..0000000 --- a/scss/application.scss +++ /dev/null @@ -1,461 +0,0 @@ -@import 'code'; -@import 'variables'; - -html { - background-color: $color-background; - font-family: $font-serif; -} - -* { - box-sizing: border-box; - - // ask mobile browsers to please trust our judgement about font sizes - -webkit-text-size-adjust: 100%; - -moz-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} - -html, body { - margin: 0; - padding: 0; - height: 100%; - width: 100%; -} - -body { - display: flex; - flex-direction: column; - width: 100%; - min-height: 100%; -} - -// ----------------------------------------------------------------------------- -// Headings - -h1, h2, h3, h4, h5, h6 { - font-family: $font-sans-serif; - font-weight: 400; -} - -h1 { - font-size: 2em; - font-weight: 700; - line-height: 1.4; - margin-bottom: 0.4em; - - @media (min-width: 575px) { - font-size: 2.3em; - } -} - -h2 { - font-size: 1.75em; - line-height: 1.5; - margin-top: 2em; - - @media (min-width: 800px) { - font-size: 2em; - } -} - -h3 { font-size: 1.5em; } -h4 { font-size: 1.17em; } -h5 { font-size: 1.1em; } - -// ----------------------------------------------------------------------------- - -a { - text-decoration: none; - transition: color 0.15s ease-in-out; -} - -p, article li, blockquote { - margin-bottom: 1.2em; - - @media (min-width: 800px) { - margin-bottom: 2em; - } -} - -body > section[role=main] { - $content-padding: 30px; - - color: $text-color-primary; - flex: 1; - font-size: 1.1em; - font-weight: 300; - line-height: 2; - padding: 0 $content-padding; - - @media (max-width: 550px) { - font-size: 0.9em; - } - - & > .content { - margin: 0 auto; - padding: 40px 0; - - @media (max-width: 550px) { - padding: 30px 0; - } - } - - article { - // When there is enough space, elements in the body are centered horizontally. - & > * { - margin-left: auto; - margin-right: auto; - max-width: $content-width-small; - } - - // Headings can spill into the margins a little bit. - & > h1, & > h2, & > h3, & > h4, & > h5, & > h6 { - max-width: $content-width-medium; - } - - // Post headers can spill into the margins a lot. - & > header { - max-width: $content-width-large; - - .date-and-tags { - max-width: $content-width-small; - margin-left: auto; - margin-right: auto; - } - } - - // Code blocks and tables can also spill into the margins. - & > pre, & > .table-wrapper { - max-width: 100%; - width: 100%; - - // On medium-size screens, they can spill into the margins a bit. - @media (min-width: $content-width-small + $content-padding * 2) { - min-width: $content-width-small; - width: fit-content; - } - - // On large screens they can spill into the margins a ton, since we really - // don’t want them to have to scroll. - @media (min-width: $content-width-max + $content-padding * 2) { - max-width: $content-width-max; - } - } - - // End-of-post navigation links can spill far into the margins. - & > .post-navigation { - max-width: $content-width-max; - } - } - - // Subscripts and superscripts should not disrupt line spacing. - sup, sub { - line-height: 0; - } - - a { - color: $text-color-link; - - &:hover { - color: $text-color-link-light; - } - } - - blockquote { - padding: 1em 1.5em 1em 2em; - border-left: 5px solid #f3ecec; - background-color: $color-section-background; - - & > :first-child { margin-top: 0; } - & > :last-child { margin-bottom: 0; } - } - - .table-wrapper { - display: block; - margin: 1em auto; - overflow: auto; - - // enable inertial horizontal scrolling - -webkit-overflow-scrolling: touch; - - @media (max-width: 700px) { - font-size: 0.9em; - line-height: 1.9; - } - - @media (max-width: 500px) { - font-size: 0.8em; - line-height: 1.8; - } - - @media (min-width: 800px) { - margin: 2em auto; - } - - table { - border-collapse: collapse; - border-spacing: 0; - border: 3px solid $color-section-background-border; - width: 100%; - - thead tr, tr:nth-child(2n) { - background-color: $color-section-background-border; - } - - td, th { - border: 0; - padding: 0.6em 1.5em; - } - } - } - - hr { - border: none; - display: block; - margin-bottom: 2em; - margin-top: 2em; - overflow-x: hidden; - width: 100%; - - @media (min-width: 800px) { - margin-bottom: 3em; - margin-top: 3em; - } - - &::before { - content: '◆◆◆'; - color: #e2caca; - display: block; - margin: 0 auto; - text-align: center; - letter-spacing: 6em; - margin-right: -6em; - font-size: 0.9em; - } - } -} - -.date-and-tags time { - font-size: 1.5em; -} - -body > footer { - background-color: $color-primary; - color: $text-color-light; - font-family: $font-sans-serif; - font-size: 0.8em; - font-weight: 300; - padding: 10px 0; - text-align: center; - - .copyright-notice { - font-size: 1.45em; - } - - a { - color: inherit; - font-weight: bold; - text-decoration: underline; - } - - & > * { - margin: 0.8em; - } -} - -article.main { - .title { - margin-top: 0; - } - - .date-and-tags { - margin-bottom: 2em; - } -} - -article.inline { - margin-bottom: 4em; - - .date-and-tags { - margin-bottom: 1em; - } - - .read-more-text { - font-style: italic; - margin-right: 0.25em; - } -} - -.footnotes { - font-size: small; - line-height: 2; - margin-top: 4em; - - li:target { - background-color: $color-section-background-highlight; - border-radius: 3px; - // We want the background to extend past both the edges of the element and - // the list bullet itself, which is easiest to do with two overlapping shadows. - box-shadow: -1.5em 0 0 0.5em $color-section-background-highlight, - 0.5em 0 0 0.5em $color-section-background-highlight; - } -} - -article.main > footer { - margin-top: 40px; -} - -.tag-page-header { - font-weight: normal; - margin-left: auto; - margin-right: auto; - margin-top: 0; - max-width: 1000px; -} - -.navigation-bar { - align-items: center; - background-color: $color-primary; - display: flex; - font-size: 1.2em; - font-family: $font-sans-serif; - font-weight: 300; - width: 100%; - - a { - color: $text-color-light; - display: block; - letter-spacing: 0.1em; - text-transform: uppercase; - text-decoration: none; - - &:hover { color: white; } - } - - .blog-title-header { - margin-bottom: 5px; - margin-top: 5px; - font-size: 1.5em; - font-weight: inherit; - - a { - letter-spacing: 0.075em; - } - - // hide the blog title in the navbar on small screen sizes - @media (max-width: 575px) { - display: none; - } - } -} - -.navigation-items { - padding: 0; - - &.left, &.right { margin: 15px; } - &.center { flex: 1; } - - li { - display: inline-block; - margin: 0 20px; - } -} - -.pagination { - align-items: center; - display: flex; - justify-content: center; - margin-top: 4em; - padding: 0; - width: 100%; - - li { - flex: 1; - list-style: none; - margin: 0 8px; - max-width: 3em; - min-width: 3em; - text-align: center; - - @media (min-width: 450px) { - margin: 0 15px; - } - - &.disabled { - color: $color-disabled; - } - - &.pagination-number { - border: 1px solid $color-primary; - max-width: 5em; - } - } - - a { - display: block; - transition: background-color 0.25s ease-in-out; - width: 100%; - } - - li.active a, li.disabled { - cursor: default; - } - - a:hover, li.active a { - background-color: $color-primary; - color: white !important; - } -} - -.post-navigation { - display: flex; - flex-wrap: wrap; - margin-top: 4em; - padding: 0; - - li { - flex: 1 1 auto; - list-style: none; - margin-bottom: 1em; - } - - .previous { - text-align: left; - } - .next { - text-align: right; - } - - .post-title { - font-style: italic; - } -} - -div.figure, article.main > p > a { - & > img { - max-width: 100%; - } -} - -.no-line-wrapping { - white-space: nowrap; -} - -// ----------------------------------------------------------------------------- -// styles for the About Me page - -.about-me-year-events { - margin-bottom: 0.35em; - margin-top: 0em; - padding-left: 1.2em; - text-indent: -1.2em; -} - -.about-me-year-label { - font-weight: bold; - font-size: 1.1em; -} - -.about-me-year-separator { - margin: 0.25em; -} diff --git a/scss/pygments.scss b/scss/pygments.scss deleted file mode 100644 index 8041872..0000000 --- a/scss/pygments.scss +++ /dev/null @@ -1,64 +0,0 @@ -$bold: 400; -$bolder: 500; - -$keyword: #6d1717; -$literal: #BA2121; - -.pygments { - .c, .cm, .c1 { color: #9e5555 } /* Comment */ - .k { color: $keyword; font-weight: $bold } /* Keyword */ - .o { color: #666666 } /* Operator */ - .cp { color: #BC7A00 } /* Comment.Preproc */ - .cs { color: #408080; font-style: italic } /* Comment.Special */ - .gd { color: #A00000 } /* Generic.Deleted */ - .ge { font-style: italic } /* Generic.Emph */ - .gr { color: #FF0000 } /* Generic.Error */ - .gh { color: #000080; font-weight: $bolder } /* Generic.Heading */ - .gi { color: #00A000 } /* Generic.Inserted */ - .go { color: #808080 } /* Generic.Output */ - .gp { color: #000080; font-weight: $bolder } /* Generic.Prompt */ - .gs { font-weight: $bolder } /* Generic.Strong */ - .gu { color: #800080; font-weight: $bolder } /* Generic.Subheading */ - .gt { color: #0040D0 } /* Generic.Traceback */ - .kd { color: #ab2626; } /* Keyword.Declaration */ - .kn { color: $keyword; font-weight: $bolder } /* Keyword.Namespace */ - .kp { color: #008000 } /* Keyword.Pseudo */ - .kr { color: #A10327; font-weight: $bolder } /* Keyword.Reserved */ - .kt, .kc, .nc { color: #B00040 } /* Keyword.Type, Keyword.Constant, Name.Class */ - .m { color: #666666 } /* Literal.Number */ - .s { color: $literal } /* Literal.String */ - .na { color: #960037 } /* Name.Attribute */ - .nb { font-weight: $bold } /* Name.Builtin */ - .nc { font-weight: $bold } /* Name.Class */ - .no { color: #880000 } /* Name.Constant */ - .nd { color: #AA22FF } /* Name.Decorator */ - .ni { color: #999999; font-weight: $bolder } /* Name.Entity */ - .ne { color: #D2413A; font-weight: $bolder } /* Name.Exception */ - .nf { color: #960037 } /* Name.Function */ - .nl { color: #A0A000 } /* Name.Label */ - .nn { color: $keyword; font-weight: $bolder } /* Name.Namespace */ - .nt { color: #008000; font-weight: $bolder } /* Name.Tag */ - .nv { color: #1c7805 } /* Name.Variable */ - .ow { color: #666666; font-weight: $bolder } /* Operator.Word */ - .w { color: #bbbbbb } /* Text.Whitespace */ - .mf { color: #666666 } /* Literal.Number.Float */ - .mh { color: #666666 } /* Literal.Number.Hex */ - .mi { color: #666666 } /* Literal.Number.Integer */ - .mo { color: #666666 } /* Literal.Number.Oct */ - .sb { color: $literal } /* Literal.String.Backtick */ - .sc { color: $literal } /* Literal.String.Char */ - .sd { color: $literal; font-style: italic } /* Literal.String.Doc */ - .s2 { color: $literal } /* Literal.String.Double */ - .se { color: $literal; font-weight: $bold } /* Literal.String.Escape */ - .sh { color: $literal } /* Literal.String.Heredoc */ - .si { color: $literal; font-weight: $bold } /* Literal.String.Interpol */ - .sx { color: #008000 } /* Literal.String.Other */ - .sr { color: #BB6688 } /* Literal.String.Regex */ - .s1 { color: $literal } /* Literal.String.Single */ - .ss { color: #19177C } /* Literal.String.Symbol */ - .bp { color: #008000 } /* Name.Builtin.Pseudo */ - .vc { color: #19177C } /* Name.Variable.Class */ - .vg { color: #19177C } /* Name.Variable.Global */ - .vi { color: #19177C } /* Name.Variable.Instance */ - .il { color: #666666 } /* Literal.Number.Integer.Long */ -} diff --git a/sitemap.txt b/sitemap.txt new file mode 100644 index 0000000..6d4620e --- /dev/null +++ b/sitemap.txt @@ -0,0 +1,33 @@ +https://lexi-lambda.github.io/about.html +https://lexi-lambda.github.io/blog/2015/07/18/automatically-deploying-a-frog-powered-blog-to-github-pages/ +https://lexi-lambda.github.io/blog/2015/08/22/deploying-racket-applications-on-heroku/ +https://lexi-lambda.github.io/blog/2015/08/30/managing-application-configuration-with-envy/ +https://lexi-lambda.github.io/blog/2015/09/23/canonical-factories-for-testing-with-factory-girl-api/ +https://lexi-lambda.github.io/blog/2015/11/06/functionally-updating-record-types-in-elm/ +https://lexi-lambda.github.io/blog/2015/12/21/adts-in-typed-racket-with-macros/ +https://lexi-lambda.github.io/blog/2016/02/18/simple-safe-multimethods-in-racket/ +https://lexi-lambda.github.io/blog/2016/06/12/four-months-with-haskell/ +https://lexi-lambda.github.io/blog/2016/08/11/climbing-the-infinite-ladder-of-abstraction/ +https://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/ +https://lexi-lambda.github.io/blog/2016/10/03/using-types-to-unit-test-in-haskell/ +https://lexi-lambda.github.io/blog/2017/01/02/rascal-a-haskell-with-more-parentheses/ +https://lexi-lambda.github.io/blog/2017/01/05/rascal-is-now-hackett-plus-some-answers-to-questions/ +https://lexi-lambda.github.io/blog/2017/04/28/lifts-for-free-making-mtl-typeclasses-derivable/ +https://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-metaprogrammable-haskell/ +https://lexi-lambda.github.io/blog/2017/06/29/unit-testing-effectful-haskell-with-monad-mock/ +https://lexi-lambda.github.io/blog/2017/08/12/user-programmable-infix-operators-in-racket/ +https://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-documentation-quality-of-life-and-snake/ +https://lexi-lambda.github.io/blog/2017/10/27/a-space-of-their-own-adding-a-type-namespace-to-hackett/ +https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/ +https://lexi-lambda.github.io/blog/2018/04/15/reimplementing-hackett-s-type-language-expanding-to-custom-core-forms-in-racket/ +https://lexi-lambda.github.io/blog/2018/09/13/custom-core-forms-in-racket-part-ii-generalizing-to-arbitrary-expressions-and-internal-definitions/ +https://lexi-lambda.github.io/blog/2018/10/06/macroexpand-anywhere-with-local-apply-transformer/ +https://lexi-lambda.github.io/blog/2019/04/21/defeating-racket-s-separate-compilation-guarantee/ +https://lexi-lambda.github.io/blog/2019/09/07/demystifying-monadbasecontrol/ +https://lexi-lambda.github.io/blog/2019/10/19/empathy-and-subjective-experience-in-programming-languages/ +https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/ +https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/ +https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playing-god-with-static-types/ +https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/ +https://lexi-lambda.github.io/blog/2021/03/25/an-introduction-to-typeclass-metaprogramming/ +https://lexi-lambda.github.io/blog/2025/05/29/a-break-from-programming-languages/ diff --git a/tags/12factor.html b/tags/12factor.html new file mode 100644 index 0000000..bcd3091 --- /dev/null +++ b/tags/12factor.html @@ -0,0 +1 @@ +Posts tagged ‘12factor’ | Alexis King’s Blog

                                            Posts tagged 12factor

                                            Managing application configuration with Envy

                                            ⦿ envy, racket, 12factor

                                            Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, The Twelve-Factor App provides a set of standards for keeping web apps sane, and one of those guidelines advises keeping configuration in the environment.

                                            Envy is the declarative bridge between Racket code and the outside world of the environment.

                                            Read more

                                            Deploying Racket applications on Heroku

                                            ⦿ racket, heroku, 12factor

                                            Heroku is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/angular.html b/tags/angular.html new file mode 100644 index 0000000..1dee3b9 --- /dev/null +++ b/tags/angular.html @@ -0,0 +1 @@ +Posts tagged ‘angular’ | Alexis King’s Blog

                                            Posts tagged angular

                                            Canonical factories for testing with factory_girl_api

                                            ⦿ ruby, rails, javascript, angular

                                            Modern web applications are often built as single-page apps, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.

                                            To attempt to address a fraction of this problem, I built factory_girl_api, a way to share context setup between both sides of the application.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/elm.html b/tags/elm.html new file mode 100644 index 0000000..f64ab09 --- /dev/null +++ b/tags/elm.html @@ -0,0 +1 @@ +Posts tagged ‘elm’ | Alexis King’s Blog

                                            Posts tagged elm

                                            Functionally updating record types in Elm

                                            ⦿ elm

                                            Elm is a wonderful language for building web apps, and I love so much of its approach to language design. Elm does so many things right straight out of the box, and that's a real breath of fresh air in the intersection of functional programming and web development. Still, it gets one thing wrong, and unfortunately, that one thing is incredibly important. Elm took the "functions" out of "functional record types".

                                            Almost any software program, at its core, is all about data. Maybe it's about computing data, maybe it's about manipulating data, or maybe it's about displaying data, but at the end of the day, some sort of data model is going to be needed. The functional model is a breathtakingly elegant system for handling data and shuttling it around throughout a program, and functional reactive programming, which Elm uses to model event-like interactions, makes this model work even better. The really important thing, though, is what tools Elm actually gives you to model your data.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/envy.html b/tags/envy.html new file mode 100644 index 0000000..923cee1 --- /dev/null +++ b/tags/envy.html @@ -0,0 +1 @@ +Posts tagged ‘envy’ | Alexis King’s Blog

                                            Posts tagged envy

                                            Managing application configuration with Envy

                                            ⦿ envy, racket, 12factor

                                            Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, The Twelve-Factor App provides a set of standards for keeping web apps sane, and one of those guidelines advises keeping configuration in the environment.

                                            Envy is the declarative bridge between Racket code and the outside world of the environment.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/frog.html b/tags/frog.html new file mode 100644 index 0000000..51f5948 --- /dev/null +++ b/tags/frog.html @@ -0,0 +1 @@ +Posts tagged ‘frog’ | Alexis King’s Blog

                                            Posts tagged frog

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/functional-programming.html b/tags/functional-programming.html new file mode 100644 index 0000000..923070f --- /dev/null +++ b/tags/functional-programming.html @@ -0,0 +1 @@ +Posts tagged ‘functional programming’ | Alexis King’s Blog

                                            Posts tagged functional programming

                                            An introduction to typeclass metaprogramming

                                            Typeclass metaprogramming is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the servant ecosystem), and it is the core mechanism used to implement generic programming via GHC generics. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.

                                            This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does not attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also not a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.

                                            Read more

                                            Names are not type safety

                                            Haskell programmers spend a lot of time talking about type safety. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published Parse, Don’t Validate as an initial stab towards bridging that gap.

                                            The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s newtype construct. The idea is simple enough—the newtype keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this sounds like a simple and straightforward path to type safety. For example, one might consider using a newtype declaration to define a type for an email address:

                                            newtype EmailAddress = EmailAddress Text

                                            This technique can provide some value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct kind of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.

                                            And names are not type safety.

                                            Read more

                                            Types as axioms, or: playing god with static types

                                            Just what exactly is a type?

                                            A common perspective is that types are restrictions. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t really predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.

                                            But that is not the only perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves you.

                                            …no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.

                                            Read more

                                            No, dynamic type systems are not inherently more open

                                            Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.

                                            This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are not about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.

                                            Read more

                                            Parse, don’t validate

                                            Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.

                                            However, about a month ago, I was reflecting on Twitter about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:

                                            Parse, don’t validate.

                                            Read more

                                            Four months with Haskell

                                            At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.

                                            Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an incredible programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/hackett.html b/tags/hackett.html new file mode 100644 index 0000000..56e0914 --- /dev/null +++ b/tags/hackett.html @@ -0,0 +1 @@ +Posts tagged ‘hackett’ | Alexis King’s Blog

                                            Posts tagged hackett

                                            Reimplementing Hackett’s type language: expanding to custom core forms in Racket

                                            ⦿ racket, hackett, macros

                                            In the past couple of weeks, I completely rewrote the implementation of Hackett’s type language to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.

                                            This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like my previous blog post on Hackett, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.

                                            Read more

                                            A space of their own: adding a type namespace to Hackett

                                            As previously discussed on this blog, my programming language, Hackett, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?

                                            For now, at least, the answer is that Hackett will emulate Haskell: Hackett now has two namespaces. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.

                                            Read more

                                            Hackett progress report: documentation, quality of life, and snake

                                            ⦿ hackett, racket, haskell

                                            Three months ago, I wrote a blog post describing my new, prototype implementation of my programming language, Hackett. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce the first (albeit quite incomplete) approach to Hackett’s documentation.

                                            I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.

                                            Read more

                                            User-programmable infix operators in Racket

                                            ⦿ racket, hackett, macros

                                            Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in Hackett, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.

                                            Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done without modifying the stock #lang racket reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.

                                            Read more

                                            Realizing Hackett, a metaprogrammable Haskell

                                            Almost five months ago, I wrote a blog post about my new programming language, Hackett, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.

                                            Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, Hackett is not only real, it’s working, and you can try it out yourself!

                                            Read more

                                            Rascal is now Hackett, plus some answers to questions

                                            Since I published my blog post introducing Rascal, I’ve gotten some amazing feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that Rascal is a language that already exists. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, Rascal is now known as Hackett.

                                            With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/haskell-2.html b/tags/haskell-2.html new file mode 100644 index 0000000..e9bc49d --- /dev/null +++ b/tags/haskell-2.html @@ -0,0 +1 @@ +Posts tagged ‘haskell’ | Alexis King’s Blog

                                            Posts tagged haskell

                                            Realizing Hackett, a metaprogrammable Haskell

                                            Almost five months ago, I wrote a blog post about my new programming language, Hackett, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.

                                            Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, Hackett is not only real, it’s working, and you can try it out yourself!

                                            Read more

                                            Lifts for free: making mtl typeclasses derivable

                                            ⦿ haskell

                                            Perhaps the most important abstraction a Haskell programmer must understand to effectively write modern Haskell code, beyond the level of the monad, is the monad transformer, a way to compose monads together in a limited fashion. One frustrating downside to monad transformers is a proliferation of lifts, which explicitly indicate which monad in a transformer “stack” a particular computation should run in. Fortunately, the venerable mtl provides typeclasses that make this lifting mostly automatic, using typeclass machinery to insert lift where appropriate.

                                            Less fortunately, the mtl approach does not actually eliminate lift entirely, it simply moves it from use sites to instances. This requires a small zoo of extraordinarily boilerplate-y instances, most of which simply implement each typeclass method using lift. While we cannot eliminate the instances entirely without somewhat dangerous techniques like overlapping instances, we can automatically derive them using features of modern GHC, eliminating the truly unnecessary boilerplate.

                                            Read more

                                            Rascal is now Hackett, plus some answers to questions

                                            Since I published my blog post introducing Rascal, I’ve gotten some amazing feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that Rascal is a language that already exists. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, Rascal is now known as Hackett.

                                            With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.

                                            Read more

                                            Using types to unit-test in Haskell

                                            ⦿ haskell, testing

                                            Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.

                                            Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.

                                            Read more

                                            Climbing the infinite ladder of abstraction

                                            I started programming in elementary school.

                                            When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to solve the general problem. When I learned about programming, I was immediately hooked: it was so easy to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.

                                            Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of abstraction.

                                            Read more

                                            Four months with Haskell

                                            At the end of January of this year, I switched to a new job, almost exclusively because I was enticed by the idea of being able to write Haskell. The concept of using such an interesting programming language every day instead of what I’d been doing before (mostly Rails and JavaScript) was very exciting, and I’m pleased to say that the switch seems to have been well worth it.

                                            Haskell was a language I had played with in the past but never really used for anything terribly practical, but lately I think I can confidently say that it really is an incredible programming language. At the same time, it has some significant drawbacks, too, though probably not the ones people expect. I certainly wasn’t prepared for some of the areas where Haskell would blow me away, nor was I capable of realizing which parts would leave me hopelessly frustrated until I actually sat down and started writing lots and lots of code.

                                            Read more

                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/haskell.html b/tags/haskell.html new file mode 100644 index 0000000..1de9288 --- /dev/null +++ b/tags/haskell.html @@ -0,0 +1 @@ +Posts tagged ‘haskell’ | Alexis King’s Blog

                                            Posts tagged haskell

                                            An introduction to typeclass metaprogramming

                                            Typeclass metaprogramming is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the servant ecosystem), and it is the core mechanism used to implement generic programming via GHC generics. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.

                                            This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does not attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also not a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.

                                            Read more

                                            Names are not type safety

                                            Haskell programmers spend a lot of time talking about type safety. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published Parse, Don’t Validate as an initial stab towards bridging that gap.

                                            The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s newtype construct. The idea is simple enough—the newtype keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this sounds like a simple and straightforward path to type safety. For example, one might consider using a newtype declaration to define a type for an email address:

                                            newtype EmailAddress = EmailAddress Text

                                            This technique can provide some value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct kind of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.

                                            And names are not type safety.

                                            Read more

                                            Types as axioms, or: playing god with static types

                                            Just what exactly is a type?

                                            A common perspective is that types are restrictions. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t really predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.

                                            But that is not the only perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves you.

                                            …no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.

                                            Read more

                                            No, dynamic type systems are not inherently more open

                                            Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.

                                            This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are not about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.

                                            Read more

                                            Parse, don’t validate

                                            Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.

                                            However, about a month ago, I was reflecting on Twitter about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:

                                            Parse, don’t validate.

                                            Read more

                                            Empathy and subjective experience in programming languages

                                            A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.

                                            Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?

                                            I think about that question a lot.

                                            Read more

                                            Demystifying MonadBaseControl

                                            ⦿ haskell

                                            MonadBaseControl from the monad-control package is a confusing typeclass, and its methods have complicated types. For many people, it’s nothing more than scary, impossible-to-understand magic that is, for some reason, needed when lifting certain kinds of operations. Few resources exist that adequately explain how, why, and when it works, which sadly seems to have resulted in some FUD about its use.

                                            There’s no doubt that the machinery of MonadBaseControl is complex, and the role it plays in practice is often subtle. However, its essence is actually much simpler than it appears, and I promise it can be understood by mere mortals. In this blog post, I hope to provide a complete survey of MonadBaseControl—how it works, how it’s designed, and how it can go wrong—in a way that is accessible to anyone with a firm grasp of monads and monad transformers. To start, we’ll motivate MonadBaseControl by reinventing it ourselves.

                                            Read more

                                            An opinionated guide to Haskell in 2018

                                            ⦿ haskell

                                            For me, this month marks the end of an era in my life: as of February 2018, I am no longer employed writing Haskell. It’s been a fascinating two years, and while I am excitedly looking forward to what I’ll be doing next, it’s likely I will continue to write Haskell in my spare time. I’ll probably even write it again professionally in the future.

                                            In the meantime, in the interest of both sharing with others the small amount of wisdom I’ve gained and preserving it for my future self, I’ve decided to write a long, rather dry overview of a few select parts of the Haskell workflow I developed and the ecosystem I settled into. This guide is, as the title notes, opinionated—it is what I used in my day-to-day work, nothing more—and I don’t claim that anything here is the only way to write Haskell, nor even the best way. It is merely what I found helpful and productive. Take from it as much or as little as you’d like.

                                            Read more

                                            Hackett progress report: documentation, quality of life, and snake

                                            ⦿ hackett, racket, haskell

                                            Three months ago, I wrote a blog post describing my new, prototype implementation of my programming language, Hackett. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce the first (albeit quite incomplete) approach to Hackett’s documentation.

                                            I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.

                                            Read more

                                            Unit testing effectful Haskell with monad-mock

                                            ⦿ haskell, testing

                                            Nearly eight months ago, I wrote a blog post about unit testing effectful Haskell code using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, monad-mock.

                                            Read more

                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/heroku.html b/tags/heroku.html new file mode 100644 index 0000000..7587010 --- /dev/null +++ b/tags/heroku.html @@ -0,0 +1 @@ +Posts tagged ‘heroku’ | Alexis King’s Blog

                                            Posts tagged heroku

                                            Deploying Racket applications on Heroku

                                            ⦿ racket, heroku, 12factor

                                            Heroku is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/javascript.html b/tags/javascript.html new file mode 100644 index 0000000..12acfda --- /dev/null +++ b/tags/javascript.html @@ -0,0 +1 @@ +Posts tagged ‘javascript’ | Alexis King’s Blog

                                            Posts tagged javascript

                                            Understanding the npm dependency model

                                            ⦿ javascript

                                            Currently, npm is the package manager for the frontend world. Sure, there are alternatives, but for the time being, npm seems to have won. Even tools like Bower are being pushed to the wayside in favor of the One True Package Manager, but what’s most interesting to me is npm’s relatively novel approach to dependency management. Unfortunately, in my experience, it is actually not particularly well understood, so consider this an attempt to clarify how exactly it works and how it affects you as a user or package developer.

                                            Read more

                                            Canonical factories for testing with factory_girl_api

                                            ⦿ ruby, rails, javascript, angular

                                            Modern web applications are often built as single-page apps, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.

                                            To attempt to address a fraction of this problem, I built factory_girl_api, a way to share context setup between both sides of the application.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/macros.html b/tags/macros.html new file mode 100644 index 0000000..8c97983 --- /dev/null +++ b/tags/macros.html @@ -0,0 +1,14 @@ +Posts tagged ‘macros’ | Alexis King’s Blog

                                            Posts tagged macros

                                            Defeating Racket’s separate compilation guarantee

                                            ⦿ racket, macros

                                            Being a self-described programming-language programming language is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate composable and compilable macros. One of the module system’s foundational properties is its separate compilation guarantee, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is essential for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.

                                            Yet today, in this blog post, we’re going to do exactly that.

                                            Read more

                                            Macroexpand anywhere with local-apply-transformer!

                                            ⦿ racket, macros

                                            Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only certain positions in Racket code are subject to macroexpansion.

                                            To illustrate, consider a macro that provides a Clojure-style let form:

                                            (require syntax/parse/define)
                                            +
                                            +(define-simple-macro (clj-let [{~seq x:id e:expr} ...] body:expr ...+)
                                            +  (let ([x e] ...) body ...))

                                            This can be used anywhere an expression is expected, and it does as one would expect:

                                            > (clj-let [x 1
                                            +            y 2]
                                            +    (+ x y))
                                            +3

                                            However, a novice macro programmer might realize that clj-let really only modifies the syntax of binding pairs for a let form. Therefore, could one define a macro that only adjusts the binding pairs of some existing let form instead of expanding to an entire let? That is, could one write the above example like this:

                                            (define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...])
                                            +  ([x e] ...))
                                            +
                                            +> (let (clj-binding-pairs
                                            +        [x 1
                                            +         y 2])
                                            +    (+ x y))
                                            +3

                                            The answer is no: the binding pairs of a let form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion anywhere in a Racket program.

                                            Read more

                                            Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions

                                            ⦿ racket, macros

                                            In my previous blog post, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.

                                            Read more

                                            Reimplementing Hackett’s type language: expanding to custom core forms in Racket

                                            ⦿ racket, hackett, macros

                                            In the past couple of weeks, I completely rewrote the implementation of Hackett’s type language to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.

                                            This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like my previous blog post on Hackett, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.

                                            Read more

                                            User-programmable infix operators in Racket

                                            ⦿ racket, hackett, macros

                                            Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in Hackett, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.

                                            Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done without modifying the stock #lang racket reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.

                                            Read more

                                            Simple, safe multimethods in Racket

                                            ⦿ racket, macros

                                            Racket ships with racket/generic, a system for defining generic methods, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports single dispatch. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.

                                            Read more

                                            ADTs in Typed Racket with macros

                                            ⦿ racket, typed racket, macros

                                            Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate why macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is ADTs.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/meta.html b/tags/meta.html new file mode 100644 index 0000000..4160fa8 --- /dev/null +++ b/tags/meta.html @@ -0,0 +1 @@ +Posts tagged ‘meta’ | Alexis King’s Blog

                                            Posts tagged meta

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/personal.html b/tags/personal.html new file mode 100644 index 0000000..3ef8d7c --- /dev/null +++ b/tags/personal.html @@ -0,0 +1 @@ +Posts tagged ‘personal’ | Alexis King’s Blog

                                            Posts tagged personal

                                            A break from programming languages

                                            This is a blog post I have been considering writing for a long time.

                                            People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.

                                            My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.

                                            This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for me, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why not—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/philosophy.html b/tags/philosophy.html new file mode 100644 index 0000000..e5ec830 --- /dev/null +++ b/tags/philosophy.html @@ -0,0 +1 @@ +Posts tagged ‘philosophy’ | Alexis King’s Blog

                                            Posts tagged philosophy

                                            Empathy and subjective experience in programming languages

                                            A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.

                                            Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?

                                            I think about that question a lot.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/programming-languages.html b/tags/programming-languages.html new file mode 100644 index 0000000..e44c6d0 --- /dev/null +++ b/tags/programming-languages.html @@ -0,0 +1 @@ +Posts tagged ‘programming languages’ | Alexis King’s Blog

                                            Posts tagged programming languages

                                            A break from programming languages

                                            This is a blog post I have been considering writing for a long time.

                                            People who have closely followed my work for the past few years have probably noticed that my output has gradually slowed. My last post on this blog was published over four years ago. The last talk I presented was almost two years ago, on work that I had recently finished but had started several years prior.

                                            My enthusiasm for my hobbies has always ebbed and flowed. After spending much of early last year managing a frustrating mixture of mental and physical health problems, it was certainly at an exceptional low. All the same, so often I have found I need only wait for my motivations to return, and so I decided to give myself some time. But although I’ve been grateful to have plenty of that this past year to rest, reflect, and recuperate (and if anything my mental health is now the best it’s been in years), the conclusion I find myself forced to confront is that many of the passions I once felt so strongly do not seem to be coming back.

                                            This post is a personal exploration of my feelings. It is a little self-indulgent: it is intended primarily for me, a means by which I may collect and process my thoughts. It provides me a certain closure, and it’s helped me make sense of where I ought to go next. Nevertheless, I hope it will also serve a secondary purpose: to explain why—and perhaps more importantly, why not—I have decided to close this particular chapter of my life, and what lessons I’ve learned along the way.

                                            Read more

                                            No, dynamic type systems are not inherently more open

                                            Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.

                                            This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are not about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.

                                            Read more

                                            Empathy and subjective experience in programming languages

                                            A stereotype about programmers is that they like to think in black and white. Programmers like things to be good or bad, moral or immoral, responsible or irresponsible. Perhaps there is something romantic in the idea that programmers like to be as binary as the computers they program. Reductionist? Almost certainly, but hey, laugh at yourself a bit: we probably deserve to be made fun of from time to time.

                                            Personally, I have no idea if the trope of the nuance-challenged programmer is accurate, but whether it’s a property of programmers or just humans behind a keyboard, the intensity with which we disagree with one another never ceases to amaze. Ask any group of working programmers what their least favorite programming language is, and there’s a pretty good chance things are going to get heated real fast. Why? What is it about programming that makes us feel so strongly that we are right and others are wrong, even when our experiences contradict those of tens or hundreds of thousands of others?

                                            I think about that question a lot.

                                            Read more

                                            A space of their own: adding a type namespace to Hackett

                                            As previously discussed on this blog, my programming language, Hackett, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?

                                            For now, at least, the answer is that Hackett will emulate Haskell: Hackett now has two namespaces. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.

                                            Read more

                                            Realizing Hackett, a metaprogrammable Haskell

                                            Almost five months ago, I wrote a blog post about my new programming language, Hackett, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.

                                            Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, Hackett is not only real, it’s working, and you can try it out yourself!

                                            Read more

                                            Rascal is now Hackett, plus some answers to questions

                                            Since I published my blog post introducing Rascal, I’ve gotten some amazing feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that Rascal is a language that already exists. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, Rascal is now known as Hackett.

                                            With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.

                                            Read more

                                            Climbing the infinite ladder of abstraction

                                            I started programming in elementary school.

                                            When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to solve the general problem. When I learned about programming, I was immediately hooked: it was so easy to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.

                                            Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of abstraction.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/racket-2.html b/tags/racket-2.html new file mode 100644 index 0000000..3d38b63 --- /dev/null +++ b/tags/racket-2.html @@ -0,0 +1 @@ +Posts tagged ‘racket’ | Alexis King’s Blog

                                            Posts tagged racket

                                            Climbing the infinite ladder of abstraction

                                            I started programming in elementary school.

                                            When I was young, I was fascinated by the idea of automation. I loathed doing the same repetitive task over and over again, and I always yearned for a way to solve the general problem. When I learned about programming, I was immediately hooked: it was so easy to turn repetitive tasks into automated pipelines that would free me from ever having to do the same dull, frustrating exercise ever again.

                                            Of course, one of the first things I found out once I’d started was that nothing is ever quite so simple. Before long, my solutions to eliminate repetition grew repetitive, and it became clear I spent a lot of time typing out the same things, over and over again, creating the very problem I had initially set out to destroy. It was through this that I grew interested in functions, classes, and other repetition-reducing aids, and soon enough, I discovered the wonderful world of abstraction.

                                            Read more

                                            Simple, safe multimethods in Racket

                                            ⦿ racket, macros

                                            Racket ships with racket/generic, a system for defining generic methods, functions that work differently depending on what sort of value they are supplied. I have made heavy use of this feature in my collections library, and it has worked well for my needs, but that system does have a bit of a limitation: it only supports single dispatch. Method implementations may only be chosen based on a single argument, so multiple dispatch is impossible.

                                            Read more

                                            ADTs in Typed Racket with macros

                                            ⦿ racket, typed racket, macros

                                            Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate why macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is ADTs.

                                            Read more

                                            Managing application configuration with Envy

                                            ⦿ envy, racket, 12factor

                                            Application configuration can be a pain. Modern web apps don't live on dedicated boxes, they run on VPSes somewhere in the amorphous "cloud", and keeping configuration out of your application's repository can seem like more trouble than it's worth. Fortunately, The Twelve-Factor App provides a set of standards for keeping web apps sane, and one of those guidelines advises keeping configuration in the environment.

                                            Envy is the declarative bridge between Racket code and the outside world of the environment.

                                            Read more

                                            Deploying Racket applications on Heroku

                                            ⦿ racket, heroku, 12factor

                                            Heroku is a "platform as a service" that provides an incredibly simple way to deploy simple internet applications, and I take liberal advantage of its free tier for testing out simple applications. It has support for a variety of languages built-in, but Racket is not currently among them. Fortunately, Heroku provides an interface for adding custom build processes for arbitrary types of applications, called “buildpacks”. I've built one for Racket apps, and with just a little bit of configuration, it’s possible to get a Racket webserver running on Heroku.

                                            Read more

                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/racket.html b/tags/racket.html new file mode 100644 index 0000000..11f7bb7 --- /dev/null +++ b/tags/racket.html @@ -0,0 +1,14 @@ +Posts tagged ‘racket’ | Alexis King’s Blog

                                            Posts tagged racket

                                            Defeating Racket’s separate compilation guarantee

                                            ⦿ racket, macros

                                            Being a self-described programming-language programming language is an ambitious goal. To preserve predictability while permitting linguistic extension, Racket comes equipped with a module system carefully designed to accommodate composable and compilable macros. One of the module system’s foundational properties is its separate compilation guarantee, which imposes strong, unbreakable limits on the extent of compile-time side-effects. It is essential for preserving static guarantees in a world where compiling a module can execute arbitrary code, and despite numerous unsafe trapdoors that have crept into Racket since its birth as PLT Scheme, none have ever given the programmer the ability to cheat it.

                                            Yet today, in this blog post, we’re going to do exactly that.

                                            Read more

                                            Macroexpand anywhere with local-apply-transformer!

                                            ⦿ racket, macros

                                            Racket programmers are accustomed to the language’s incredible capacity for extension and customization. Writing useful macros that do complicated things is easy, and it’s simple to add new syntactic forms to meet domain-specific needs. However, it doesn’t take long before many budding macrologists bump into the realization that only certain positions in Racket code are subject to macroexpansion.

                                            To illustrate, consider a macro that provides a Clojure-style let form:

                                            (require syntax/parse/define)
                                            +
                                            +(define-simple-macro (clj-let [{~seq x:id e:expr} ...] body:expr ...+)
                                            +  (let ([x e] ...) body ...))

                                            This can be used anywhere an expression is expected, and it does as one would expect:

                                            > (clj-let [x 1
                                            +            y 2]
                                            +    (+ x y))
                                            +3

                                            However, a novice macro programmer might realize that clj-let really only modifies the syntax of binding pairs for a let form. Therefore, could one define a macro that only adjusts the binding pairs of some existing let form instead of expanding to an entire let? That is, could one write the above example like this:

                                            (define-simple-macro (clj-binding-pairs [{~seq x:id e:expr} ...])
                                            +  ([x e] ...))
                                            +
                                            +> (let (clj-binding-pairs
                                            +        [x 1
                                            +         y 2])
                                            +    (+ x y))
                                            +3

                                            The answer is no: the binding pairs of a let form are not subject to macroexpansion, so the above attempt fails with a syntax error. In this blog post, we will examine the reasons behind this limitation, then explain how to overcome it using a solution that allows macroexpansion anywhere in a Racket program.

                                            Read more

                                            Custom core forms in Racket, part II: generalizing to arbitrary expressions and internal definitions

                                            ⦿ racket, macros

                                            In my previous blog post, I covered the process involved in creating a small language with a custom set of core forms. Specifically, it discussed what was necessary to create Hackett’s type language, which involved expanding to custom expressions. While somewhat involved, Hackett’s type language was actually a relatively simple example to use, since it only made use of a subset of the linguistic features Racket supports. In this blog post, I’ll demonstrate how that same technique can be generalized to support runtime bindings and internal definitions, two key concepts useful if intending to develop a more featureful language than Hackett’s intentionally-restrictive type system.

                                            Read more

                                            Reimplementing Hackett’s type language: expanding to custom core forms in Racket

                                            ⦿ racket, hackett, macros

                                            In the past couple of weeks, I completely rewrote the implementation of Hackett’s type language to improve the integration between the type representation and Racket’s macro system. The new type language effectively implements a way to reuse as much of the Racket macroexpanding infrastructure as possible while expanding a completely custom language, which uses a custom set of core forms. The fundamental technique used to do so is not novel, and it seems to be periodically rediscovered every so often, but it has never been published or documented anywhere, and getting it right involves understanding a great number of subtleties about the Racket macro system. While I cannot entirely eliminate the need to understand those subtleties, in this blog post, I hope to make the secret sauce considerably less secret.

                                            This blog post is both a case study on how I implemented the expander for Hackett’s new type language and a discussion of how such a technique can apply more generally. Like my previous blog post on Hackett, which covered the implementation of its namespace system, the implementation section of this blog post is highly technical and probably requires significant experience with Racket’s macro system to completely comprehend. However, the surrounding material is written to be more accessible, so even if you are not a Racket programmer, you should hopefully be able to understand the big ideas behind this change.

                                            Read more

                                            A space of their own: adding a type namespace to Hackett

                                            As previously discussed on this blog, my programming language, Hackett, is a fusion of two languages, Haskell and Racket. What happens when two distinctly different programming languages collide? Hackett recently faced that very problem when it came to the question of namespacing: Haskell has two namespaces, one for values and another for types, but Racket is a staunch Lisp-1 with a single namespace for all bindings. Which convention should Hackett adopt?

                                            For now, at least, the answer is that Hackett will emulate Haskell: Hackett now has two namespaces. Of course, Hackett is embedded in Racket, so what did it take to add an entirely new namespace to a language that possesses only one? The answer was a little more than I had hoped, but it was still remarkably simple given the problem: after two weeks of hacking, I’ve managed to get something working.

                                            Read more

                                            Hackett progress report: documentation, quality of life, and snake

                                            ⦿ hackett, racket, haskell

                                            Three months ago, I wrote a blog post describing my new, prototype implementation of my programming language, Hackett. At the time, some things looked promising—the language already included algebraic datatypes, typeclasses, laziness, and even a mini, proof of concept web server. It was, however, clearly still rather rough around the edges—error messages were poor, features were sometimes brittle, the REPL experience was less than ideal, and there was no documentation to speak of. In the time since, while the language is still experimental, I have tackled a handful of those issues, and I am excited to announce the first (albeit quite incomplete) approach to Hackett’s documentation.

                                            I’d recommend clicking that link above and at least skimming around before reading the rest of this blog post, as its remainder will describe some of the pieces that didn’t end up in the documentation: the development process, the project’s status, a small demo, and some other details from behind the scenes.

                                            Read more

                                            User-programmable infix operators in Racket

                                            ⦿ racket, hackett, macros

                                            Lisps are not known for infix operators, quite the opposite; infix operators generally involve more syntax and parsing than Lispers are keen to support. However, in Hackett, all functions are curried, and variable-arity functions do not exist. Infix operators are almost necessary for that to be palatable, and though there are other reasons to want them, it may not be obvious how to support them without making the reader considerably more complex.

                                            Fortunately, if we require users to syntactically specify where they wish to use infix expressions, support for infix operators is not only possible, but can support be done without modifying the stock #lang racket reader. Futhermore, the resulting technique makes it possible for fixity information to be specified locally in a way that cooperates nicely with the Racket macro system, allowing the parsing of infix expressions to be manipulated at compile-time by users’ macros.

                                            Read more

                                            Realizing Hackett, a metaprogrammable Haskell

                                            Almost five months ago, I wrote a blog post about my new programming language, Hackett, a fanciful sketch of a programming language from a far-off land with Haskell’s type system and Racket’s macros. At that point in time, I had a little prototype that barely worked, that I barely understood, and was a little bit of a technical dead-end. People saw the post, they got excited, but development sort of stopped.

                                            Then, almost two months ago, I took a second stab at the problem in earnest. I read a lot, I asked a lot of people for help, and eventually I got something sort of working. Suddenly, Hackett is not only real, it’s working, and you can try it out yourself!

                                            Read more

                                            Rascal is now Hackett, plus some answers to questions

                                            Since I published my blog post introducing Rascal, I’ve gotten some amazing feedback, more than I had ever anticipated! One of the things that was pointed out, though, is that Rascal is a language that already exists. Given that the name “Rascal” came from a mixture of “Racket” and “Haskell”, I always had an alternative named planned, and that’s “Hackett”. So, to avoid confusion as much as possible, Rascal is now known as Hackett.

                                            With that out of the way, I also want to answer some of the other questions I received, both to hopefully clear up some confusion and to have something I can point to if I get the same questions in the future.

                                            Read more

                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/rails.html b/tags/rails.html new file mode 100644 index 0000000..5b8e44a --- /dev/null +++ b/tags/rails.html @@ -0,0 +1 @@ +Posts tagged ‘rails’ | Alexis King’s Blog

                                            Posts tagged rails

                                            Canonical factories for testing with factory_girl_api

                                            ⦿ ruby, rails, javascript, angular

                                            Modern web applications are often built as single-page apps, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.

                                            To attempt to address a fraction of this problem, I built factory_girl_api, a way to share context setup between both sides of the application.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/ruby.html b/tags/ruby.html new file mode 100644 index 0000000..95bb6d1 --- /dev/null +++ b/tags/ruby.html @@ -0,0 +1 @@ +Posts tagged ‘ruby’ | Alexis King’s Blog

                                            Posts tagged ruby

                                            Canonical factories for testing with factory_girl_api

                                            ⦿ ruby, rails, javascript, angular

                                            Modern web applications are often built as single-page apps, which are great for keeping concerns separated, but problematic when tested. Logic needs to be duplicated in front- and back-end test suites, and if the two apps diverge, the tests won't catch the failure. I haven't found a very good solution to this problem aside from brittle, end-to-end integration tests.

                                            To attempt to address a fraction of this problem, I built factory_girl_api, a way to share context setup between both sides of the application.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/testing.html b/tags/testing.html new file mode 100644 index 0000000..5890256 --- /dev/null +++ b/tags/testing.html @@ -0,0 +1 @@ +Posts tagged ‘testing’ | Alexis King’s Blog

                                            Posts tagged testing

                                            Unit testing effectful Haskell with monad-mock

                                            ⦿ haskell, testing

                                            Nearly eight months ago, I wrote a blog post about unit testing effectful Haskell code using a library called test-fixture. That library has served us well, but it wasn’t as easy to use as I would have liked, and it worked better with certain patterns than others. Since then, I’ve learned more about Haskell and more about testing, and I’m pleased to announce that I am releasing an entirely new testing library, monad-mock.

                                            Read more

                                            Using types to unit-test in Haskell

                                            ⦿ haskell, testing

                                            Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in the form of classes and interfaces. These boundaries make it easy to stub out parts of a system to test functionality in isolation, which makes it possible to write fast, deterministic test suites that are robust in the face of change. When writing Haskell, it can be unclear how to accomplish the same goals: even inside pure code, it can become difficult to test a particular code path without also testing all its collaborators.

                                            Fortunately, by taking advantage of Haskell’s expressive type system, it’s possible to not only achieve parity with object-oriented testing techniques, but also to provide stronger static guarantees as well. Furthermore, it’s all possible without resorting to extra-linguistic hacks that static object-oriented languages sometimes use for mocking, such as dynamic bytecode generation.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/typed-racket.html b/tags/typed-racket.html new file mode 100644 index 0000000..10ed17a --- /dev/null +++ b/tags/typed-racket.html @@ -0,0 +1 @@ +Posts tagged ‘typed racket’ | Alexis King’s Blog

                                            Posts tagged typed racket

                                            ADTs in Typed Racket with macros

                                            ⦿ racket, typed racket, macros

                                            Macros are one of Racket's flagship features, and its macro system really is state of the art. Of course, it can sometimes be difficult to demonstrate why macros are so highly esteemed, in part because it can be hard to find self-contained examples of using macros in practice. Of course, one thing that macros are perfect for is filling a "hole" in the language by introducing a feature a language lacks, and one of those features in Typed Racket is ADTs.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/types.html b/tags/types.html new file mode 100644 index 0000000..3cad628 --- /dev/null +++ b/tags/types.html @@ -0,0 +1 @@ +Posts tagged ‘types’ | Alexis King’s Blog

                                            Posts tagged types

                                            An introduction to typeclass metaprogramming

                                            Typeclass metaprogramming is a powerful technique available to Haskell programmers to automatically generate term-level code from static type information. It has been used to great effect in several popular Haskell libraries (such as the servant ecosystem), and it is the core mechanism used to implement generic programming via GHC generics. Despite this, remarkably little material exists that explains the technique, relegating it to folk knowledge known only to advanced Haskell programmers.

                                            This blog post attempts to remedy that by providing an overview of the foundational concepts behind typeclass metaprogramming. It does not attempt to be a complete guide to type-level programming in Haskell—such a task could easily fill a book—but it does provide explanations and illustrations of the most essential components. This is also not a blog post for Haskell beginners—familiarity with the essentials of the Haskell type system and several common GHC extensions is assumed—but it does not assume any prior knowledge of type-level programming.

                                            Read more

                                            Names are not type safety

                                            Haskell programmers spend a lot of time talking about type safety. The Haskell school of program construction advocates “capturing invariants in the type system” and “making illegal states unrepresentable,” both of which sound like compelling goals, but are rather vague on the techniques used to achieve them. Almost exactly one year ago, I published Parse, Don’t Validate as an initial stab towards bridging that gap.

                                            The ensuing discussions were largely productive and right-minded, but one particular source of confusion quickly became clear: Haskell’s newtype construct. The idea is simple enough—the newtype keyword declares a wrapper type, nominally distinct from but representationally equivalent to the type it wraps—and on the surface this sounds like a simple and straightforward path to type safety. For example, one might consider using a newtype declaration to define a type for an email address:

                                            newtype EmailAddress = EmailAddress Text

                                            This technique can provide some value, and when coupled with a smart constructor and an encapsulation boundary, it can even provide some safety. But it is a meaningfully distinct kind of type safety from the one I highlighted a year ago, one that is far weaker. On its own, a newtype is just a name.

                                            And names are not type safety.

                                            Read more

                                            Types as axioms, or: playing god with static types

                                            Just what exactly is a type?

                                            A common perspective is that types are restrictions. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t really predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.

                                            But that is not the only perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves you.

                                            …no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.

                                            Read more

                                            No, dynamic type systems are not inherently more open

                                            Internet debates about typing disciplines continue to be plagued by a pervasive myth that dynamic type systems are inherently better at modeling “open world” domains. The argument usually goes like this: the goal of static typing is to pin everything down as much as possible, but in the real world, that just isn’t practical. Real systems should be loosely coupled and worry about data representation as little as possible, so dynamic types lead to a more robust system in the large.

                                            This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are not about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t. Indeed, in practice static type systems excel at processing data with only a partially-known structure, as they can be used to ensure application logic doesn’t accidentally assume too much.

                                            Read more

                                            Parse, don’t validate

                                            Historically, I’ve struggled to find a concise, simple way to explain what it means to practice type-driven design. Too often, when someone asks me “How did you come up with this approach?” I find I can’t give them a satisfying answer. I know it didn’t just come to me in a vision—I have an iterative design process that doesn’t require plucking the “right” approach out of thin air—yet I haven’t been very successful in communicating that process to others.

                                            However, about a month ago, I was reflecting on Twitter about the differences I experienced parsing JSON in statically- and dynamically-typed languages, and finally, I realized what I was looking for. Now I have a single, snappy slogan that encapsulates what type-driven design means to me, and better yet, it’s only three words long:

                                            Parse, don’t validate.

                                            Read more

                                            • 1
                                            Built with Scribble, the Racket document preparation system.
                                            Feeds are available via Atom or RSS.
                                            \ No newline at end of file diff --git a/tags/typescript.html b/tags/typescript.html new file mode 100644 index 0000000..72a18c0 --- /dev/null +++ b/tags/typescript.html @@ -0,0 +1 @@ +Posts tagged ‘typescript’ | Alexis King’s Blog

                                            Posts tagged typescript

                                            Types as axioms, or: playing god with static types

                                            Just what exactly is a type?

                                            A common perspective is that types are restrictions. Static types restrict the set of values a variable may contain, capturing some subset of the space of “all possible values.” Under this worldview, a typechecker is sort of like an oracle, predicting which values will end up where when the program runs and making sure they satisfy the constraints the programmer wrote down in the type annotations. Of course, the typechecker can’t really predict the future, so when the typechecker gets it wrong—it can’t “figure out” what a value will be—static types can feel like self-inflicted shackles.

                                            But that is not the only perspective. There is another way—a way that puts you, the programmer, back in the driver’s seat. You make the rules, you call the shots, you set the objectives. You need not be limited any longer by what the designers of your programming language decided the typechecker can and cannot prove. You do not serve the typechecker; the typechecker serves you.

                                            …no, I’m not trying to sell you a dubious self-help book for programmers who feel like they’ve lost control of their lives. If the above sounds too good to be true, well… I won’t pretend it’s all actually as easy as I make it sound. Nevertheless, it’s well within the reach of the working programmer, and most remarkably, all it takes is a change in perspective.

                                            Read more

                                            • 1
                                            \ No newline at end of file diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 52f8105..0000000 --- a/yarn.lock +++ /dev/null @@ -1,4537 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/cli@^7.4.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.14.5.tgz#9551b194f02360729de6060785bbdcce52c69f0a" - integrity sha512-poegjhRvXHWO0EAsnYajwYZuqcz7gyfxwfaecUESxDujrqOivf3zrjFbub8IJkrqEaz3fvJWh001EzxBub54fg== - dependencies: - commander "^4.0.1" - convert-source-map "^1.1.0" - fs-readdir-recursive "^1.1.0" - glob "^7.0.0" - make-dir "^2.1.0" - slash "^2.0.0" - source-map "^0.5.0" - optionalDependencies: - "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.2" - chokidar "^3.4.0" - -"@babel/code-frame@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.5.tgz#8ef4c18e58e801c5c95d3c1c0f2874a2680fadea" - integrity sha512-kixrYn4JwfAVPa0f2yfzc2AWti6WRRyO3XjWW5PJAvtE11qhSayrrcrEnee05KAtNaPC+EwehE8Qt1UedEVB8w== - -"@babel/core@^7.4.3": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.6.tgz#e0814ec1a950032ff16c13a2721de39a8416fcab" - integrity sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helpers" "^7.14.6" - "@babel/parser" "^7.14.6" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/generator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" - integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== - dependencies: - "@babel/types" "^7.14.5" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/helper-annotate-as-pure@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61" - integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz#b939b43f8c37765443a19ae74ad8b15978e0a191" - integrity sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" - integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== - dependencies: - "@babel/compat-data" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.14.5": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542" - integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-member-expression-to-functions" "^7.14.5" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - -"@babel/helper-create-regexp-features-plugin@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz#c7d5ac5e9cf621c26057722fb7a8a4c5889358c4" - integrity sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - regexpu-core "^4.7.1" - -"@babel/helper-define-polyfill-provider@^0.2.2": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" - integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-explode-assignable-expression@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz#8aa72e708205c7bb643e45c73b4386cdf2a1f645" - integrity sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-function-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" - integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== - dependencies: - "@babel/helper-get-function-arity" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-get-function-arity@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" - integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-hoist-variables@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" - integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-member-expression-to-functions@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.5.tgz#d5c70e4ad13b402c95156c7a53568f504e2fb7b8" - integrity sha512-UxUeEYPrqH1Q/k0yRku1JE7dyfyehNwT6SVkMHvYvPDv4+uu627VXBckVj891BO8ruKBkiDoGnZf4qPDD8abDQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" - integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-module-transforms@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz#7de42f10d789b423eb902ebd24031ca77cb1e10e" - integrity sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA== - dependencies: - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-simple-access" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-optimise-call-expression@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" - integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-remap-async-to-generator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.14.5.tgz#51439c913612958f54a987a4ffc9ee587a2045d6" - integrity sha512-rLQKdQU+HYlxBwQIj8dk4/0ENOUEhA/Z0l4hN8BexpvmSMN9oA9EagjnhnDpNsRdWCfjwa4mn/HyBXO9yhQP6A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-wrap-function" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-replace-supers@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" - integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.14.5" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helper-simple-access@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz#66ea85cf53ba0b4e588ba77fc813f53abcaa41c4" - integrity sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-skip-transparent-expression-wrappers@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz#96f486ac050ca9f44b009fbe5b7d394cab3a0ee4" - integrity sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-split-export-declaration@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" - integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== - dependencies: - "@babel/types" "^7.14.5" - -"@babel/helper-validator-identifier@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" - integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== - -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== - -"@babel/helper-wrap-function@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.14.5.tgz#5919d115bf0fe328b8a5d63bcb610f51601f2bff" - integrity sha512-YEdjTCq+LNuNS1WfxsDCNpgXkJaIyqco6DAelTUjT4f2KIWC1nBcaCaSdHTBqQVLnTBexBcVcFhLSU1KnYuePQ== - dependencies: - "@babel/helper-function-name" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/helpers@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.6.tgz#5b58306b95f1b47e2a0199434fa8658fa6c21635" - integrity sha512-yesp1ENQBiLI+iYHSJdoZKUtRpfTlL1grDIX9NRlAVppljLw/4tTyYupIB7uIYmC3stW/imAv8EqaKaS/ibmeA== - dependencies: - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.14.5", "@babel/parser@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.6.tgz#d85cc68ca3cac84eae384c06f032921f5227f4b2" - integrity sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ== - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz#4b467302e1548ed3b1be43beae2cc9cf45e0bb7e" - integrity sha512-ZoJS2XCKPBfTmL122iP6NM9dOg+d4lc9fFk3zxc8iDjvt8Pk4+TlsHSKhIPf6X+L5ORCdBzqMZDjL/WHj7WknQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - "@babel/plugin-proposal-optional-chaining" "^7.14.5" - -"@babel/plugin-proposal-async-generator-functions@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.5.tgz#4024990e3dd74181f4f426ea657769ff49a2df39" - integrity sha512-tbD/CG3l43FIXxmu4a7RBe4zH7MLJ+S/lFowPFO7HetS2hyOZ/0nnnznegDuzFzfkyQYTxqdTH/hKmuBngaDAA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.14.5" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz#40d1ee140c5b1e31a350f4f5eed945096559b42e" - integrity sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.5.tgz#158e9e10d449c3849ef3ecde94a03d9f1841b681" - integrity sha512-KBAH5ksEnYHCegqseI5N9skTdxgJdmDoAOc0uXa+4QMYKeZD0w5IARh4FMlTNtaHhbB8v+KzMdTgxMMzsIy6Yg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz#0c6617df461c0c1f8fff3b47cd59772360101d2c" - integrity sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz#dbad244310ce6ccd083072167d8cea83a52faf76" - integrity sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz#38de60db362e83a3d8c944ac858ddf9f0c2239eb" - integrity sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz#6e6229c2a99b02ab2915f82571e0cc646a40c738" - integrity sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz#ee38589ce00e2cc59b299ec3ea406fcd3a0fdaf6" - integrity sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz#83631bf33d9a51df184c2102a069ac0c58c05f18" - integrity sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.5.tgz#e581d5ccdfa187ea6ed73f56c6a21c1580b90fbf" - integrity sha512-VzMyY6PWNPPT3pxc5hi9LloKNr4SSrVCg7Yr6aZpW4Ym07r7KqSU/QXYwjXLVxqwSv0t/XSXkFoKBPUkZ8vb2A== - dependencies: - "@babel/compat-data" "^7.14.5" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.14.5" - -"@babel/plugin-proposal-optional-catch-binding@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz#939dd6eddeff3a67fdf7b3f044b5347262598c3c" - integrity sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz#fa83651e60a360e3f13797eef00b8d519695b603" - integrity sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz#37446495996b2945f30f5be5b60d5e2aa4f5792d" - integrity sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-proposal-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.5.tgz#9f65a4d0493a940b4c01f8aa9d3f1894a587f636" - integrity sha512-62EyfyA3WA0mZiF2e2IV9mc9Ghwxcg8YTu8BS4Wss4Y3PY725OmS9M0qLORbJwLqFtGh+jiE4wAmocK2CTUK2Q== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-create-class-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.14.5", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz#0f95ee0e757a5d647f378daa0eca7e93faa8bbe8" - integrity sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-arrow-functions@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz#f7187d9588a768dd080bf4c9ffe117ea62f7862a" - integrity sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-async-to-generator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz#72c789084d8f2094acb945633943ef8443d39e67" - integrity sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA== - dependencies: - "@babel/helper-module-imports" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-remap-async-to-generator" "^7.14.5" - -"@babel/plugin-transform-block-scoped-functions@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz#e48641d999d4bc157a67ef336aeb54bc44fd3ad4" - integrity sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-block-scoping@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz#8cc63e61e50f42e078e6f09be775a75f23ef9939" - integrity sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-classes@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.5.tgz#0e98e82097b38550b03b483f9b51a78de0acb2cf" - integrity sha512-J4VxKAMykM06K/64z9rwiL6xnBHgB1+FVspqvlgCdwD1KUbQNfszeKVVOMh59w3sztHYIZDgnhOC4WbdEfHFDA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-optimise-call-expression" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz#1b9d78987420d11223d41195461cc43b974b204f" - integrity sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-destructuring@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.5.tgz#d32ad19ff1a6da1e861dc62720d80d9776e3bf35" - integrity sha512-wU9tYisEbRMxqDezKUqC9GleLycCRoUsai9ddlsq54r8QRLaeEhc+d+9DqCG+kV9W2GgQjTZESPTpn5bAFMDww== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-dotall-regex@^7.14.5", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz#2f6bf76e46bdf8043b4e7e16cf24532629ba0c7a" - integrity sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-duplicate-keys@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz#365a4844881bdf1501e3a9f0270e7f0f91177954" - integrity sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-exponentiation-operator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz#5154b8dd6a3dfe6d90923d61724bd3deeb90b493" - integrity sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-for-of@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz#dae384613de8f77c196a8869cbf602a44f7fc0eb" - integrity sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-function-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz#e81c65ecb900746d7f31802f6bed1f52d915d6f2" - integrity sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ== - dependencies: - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz#41d06c7ff5d4d09e3cf4587bd3ecf3930c730f78" - integrity sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-member-expression-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz#b39cd5212a2bf235a617d320ec2b48bcc091b8a7" - integrity sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-modules-amd@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz#4fd9ce7e3411cb8b83848480b7041d83004858f7" - integrity sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.5.tgz#7aaee0ea98283de94da98b28f8c35701429dad97" - integrity sha512-en8GfBtgnydoao2PS+87mKyw62k02k7kJ9ltbKe0fXTHrQmG6QZZflYuGI1VVG7sVpx4E1n7KBpNlPb8m78J+A== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-simple-access" "^7.14.5" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-systemjs@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.14.5.tgz#c75342ef8b30dcde4295d3401aae24e65638ed29" - integrity sha512-mNMQdvBEE5DcMQaL5LbzXFMANrQjd2W7FPzg34Y4yEz7dBgdaC+9B84dSO+/1Wba98zoDbInctCDo4JGxz1VYA== - dependencies: - "@babel/helper-hoist-variables" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-identifier" "^7.14.5" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz#fb662dfee697cce274a7cda525190a79096aa6e0" - integrity sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA== - dependencies: - "@babel/helper-module-transforms" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.5.tgz#d537e8ee083ee6f6aa4f4eef9d2081d555746e4c" - integrity sha512-+Xe5+6MWFo311U8SchgeX5c1+lJM+eZDBZgD+tvXu9VVQPXwwVzeManMMjYX6xw2HczngfOSZjoFYKwdeB/Jvw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - -"@babel/plugin-transform-new-target@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz#31bdae8b925dc84076ebfcd2a9940143aed7dbf8" - integrity sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-object-super@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz#d0b5faeac9e98597a161a9cf78c527ed934cdc45" - integrity sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-replace-supers" "^7.14.5" - -"@babel/plugin-transform-parameters@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz#49662e86a1f3ddccac6363a7dfb1ff0a158afeb3" - integrity sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-property-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz#0ddbaa1f83db3606f1cdf4846fa1dfb473458b34" - integrity sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-regenerator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz#9676fd5707ed28f522727c5b3c0aa8544440b04f" - integrity sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg== - dependencies: - regenerator-transform "^0.14.2" - -"@babel/plugin-transform-reserved-words@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz#c44589b661cfdbef8d4300dcc7469dffa92f8304" - integrity sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-shorthand-properties@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" - integrity sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-spread@^7.14.5": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" - integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" - -"@babel/plugin-transform-sticky-regex@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz#5b617542675e8b7761294381f3c28c633f40aeb9" - integrity sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-template-literals@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz#a5f2bc233937d8453885dc736bdd8d9ffabf3d93" - integrity sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-typeof-symbol@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz#39af2739e989a2bd291bf6b53f16981423d457d4" - integrity sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-unicode-escapes@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz#9d4bd2a681e3c5d7acf4f57fa9e51175d91d0c6b" - integrity sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-unicode-regex@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz#4cd09b6c8425dd81255c7ceb3fb1836e7414382e" - integrity sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/preset-env@^7.4.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.5.tgz#c0c84e763661fd0e74292c3d511cb33b0c668997" - integrity sha512-ci6TsS0bjrdPpWGnQ+m4f+JSSzDKlckqKIJJt9UZ/+g7Zz9k0N8lYU8IeLg/01o2h8LyNZDMLGgRLDTxpudLsA== - dependencies: - "@babel/compat-data" "^7.14.5" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-validator-option" "^7.14.5" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.14.5" - "@babel/plugin-proposal-async-generator-functions" "^7.14.5" - "@babel/plugin-proposal-class-properties" "^7.14.5" - "@babel/plugin-proposal-class-static-block" "^7.14.5" - "@babel/plugin-proposal-dynamic-import" "^7.14.5" - "@babel/plugin-proposal-export-namespace-from" "^7.14.5" - "@babel/plugin-proposal-json-strings" "^7.14.5" - "@babel/plugin-proposal-logical-assignment-operators" "^7.14.5" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.5" - "@babel/plugin-proposal-numeric-separator" "^7.14.5" - "@babel/plugin-proposal-object-rest-spread" "^7.14.5" - "@babel/plugin-proposal-optional-catch-binding" "^7.14.5" - "@babel/plugin-proposal-optional-chaining" "^7.14.5" - "@babel/plugin-proposal-private-methods" "^7.14.5" - "@babel/plugin-proposal-private-property-in-object" "^7.14.5" - "@babel/plugin-proposal-unicode-property-regex" "^7.14.5" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.14.5" - "@babel/plugin-transform-async-to-generator" "^7.14.5" - "@babel/plugin-transform-block-scoped-functions" "^7.14.5" - "@babel/plugin-transform-block-scoping" "^7.14.5" - "@babel/plugin-transform-classes" "^7.14.5" - "@babel/plugin-transform-computed-properties" "^7.14.5" - "@babel/plugin-transform-destructuring" "^7.14.5" - "@babel/plugin-transform-dotall-regex" "^7.14.5" - "@babel/plugin-transform-duplicate-keys" "^7.14.5" - "@babel/plugin-transform-exponentiation-operator" "^7.14.5" - "@babel/plugin-transform-for-of" "^7.14.5" - "@babel/plugin-transform-function-name" "^7.14.5" - "@babel/plugin-transform-literals" "^7.14.5" - "@babel/plugin-transform-member-expression-literals" "^7.14.5" - "@babel/plugin-transform-modules-amd" "^7.14.5" - "@babel/plugin-transform-modules-commonjs" "^7.14.5" - "@babel/plugin-transform-modules-systemjs" "^7.14.5" - "@babel/plugin-transform-modules-umd" "^7.14.5" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.14.5" - "@babel/plugin-transform-new-target" "^7.14.5" - "@babel/plugin-transform-object-super" "^7.14.5" - "@babel/plugin-transform-parameters" "^7.14.5" - "@babel/plugin-transform-property-literals" "^7.14.5" - "@babel/plugin-transform-regenerator" "^7.14.5" - "@babel/plugin-transform-reserved-words" "^7.14.5" - "@babel/plugin-transform-shorthand-properties" "^7.14.5" - "@babel/plugin-transform-spread" "^7.14.5" - "@babel/plugin-transform-sticky-regex" "^7.14.5" - "@babel/plugin-transform-template-literals" "^7.14.5" - "@babel/plugin-transform-typeof-symbol" "^7.14.5" - "@babel/plugin-transform-unicode-escapes" "^7.14.5" - "@babel/plugin-transform-unicode-regex" "^7.14.5" - "@babel/preset-modules" "^0.1.4" - "@babel/types" "^7.14.5" - babel-plugin-polyfill-corejs2 "^0.2.2" - babel-plugin-polyfill-corejs3 "^0.2.2" - babel-plugin-polyfill-regenerator "^0.2.2" - core-js-compat "^3.14.0" - semver "^6.3.0" - -"@babel/preset-modules@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" - integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/runtime@^7.8.4": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" - integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" - integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.5.tgz#c111b0f58afab4fea3d3385a406f692748c59870" - integrity sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-hoist-variables" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/parser" "^7.14.5" - "@babel/types" "^7.14.5" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.14.5", "@babel/types@^7.4.4": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" - integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - to-fast-properties "^2.0.0" - -"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.2": - version "2.1.8-no-fsevents.2" - resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz#e324c0a247a5567192dd7180647709d7e2faf94b" - integrity sha512-Fb8WxUFOBQVl+CX4MWet5o7eCc6Pj04rXIwVKZ6h1NnqTo45eOQW6aWyhG25NIODvWFwTDMwBsYxrQ3imxpetg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^5.1.2" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -ajv@^6.12.3: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== - dependencies: - ansi-wrap "^0.1.0" - -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= - dependencies: - ansi-wrap "0.1.0" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -append-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" - integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= - dependencies: - buffer-equal "^1.0.0" - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-filter@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" - integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= - dependencies: - make-iterator "^1.0.0" - -arr-flatten@^1.0.1, arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-map@^2.0.0, arr-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" - integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= - dependencies: - make-iterator "^1.0.0" - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-each@^1.0.0, array-each@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" - integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= - -array-initial@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" - integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= - dependencies: - array-slice "^1.0.0" - is-number "^4.0.0" - -array-last@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" - integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== - dependencies: - is-number "^4.0.0" - -array-slice@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" - integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== - -array-sort@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" - integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== - dependencies: - default-compare "^1.0.0" - get-value "^2.0.6" - kind-of "^5.0.2" - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -async-done@^1.2.0, async-done@^1.2.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" - integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.2" - process-nextick-args "^2.0.0" - stream-exhaust "^1.0.1" - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -async-foreach@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" - integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= - -async-settle@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" - integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= - dependencies: - async-done "^1.2.2" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -autoprefixer@^9.5.1: - version "9.8.6" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" - integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== - dependencies: - browserslist "^4.12.0" - caniuse-lite "^1.0.30001109" - colorette "^1.2.1" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.32" - postcss-value-parser "^4.1.0" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - -babel-plugin-polyfill-corejs2@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" - integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== - dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.2.2" - semver "^6.1.1" - -babel-plugin-polyfill-corejs3@^0.2.2: - version "0.2.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz#72add68cf08a8bf139ba6e6dfc0b1d504098e57b" - integrity sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - core-js-compat "^3.14.0" - -babel-plugin-polyfill-regenerator@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" - integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.2" - -bach@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" - integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= - dependencies: - arr-filter "^1.1.1" - arr-flatten "^1.0.1" - arr-map "^2.0.0" - array-each "^1.0.0" - array-initial "^1.0.0" - array-last "^1.1.1" - async-done "^1.2.2" - async-settle "^1.0.0" - now-and-later "^2.0.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -browserslist@^4.12.0, browserslist@^4.16.6: - version "4.16.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" - integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== - dependencies: - caniuse-lite "^1.0.30001219" - colorette "^1.2.2" - electron-to-chromium "^1.3.723" - escalade "^3.1.1" - node-releases "^1.1.71" - -buffer-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" - integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -call-bind@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= - -camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001219: - version "1.0.30001239" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz#66e8669985bb2cb84ccb10f68c25ce6dd3e4d2b8" - integrity sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chokidar@^2.0.0: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chokidar@^3.4.0: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= - -clone@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -cloneable-readable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -coffeescript@^2.1.0, coffeescript@^2.4.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-2.5.1.tgz#b2442a1f2c806139669534a54adc35010559d16a" - integrity sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ== - -collection-map@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" - integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= - dependencies: - arr-map "^2.0.2" - for-own "^1.0.0" - make-iterator "^1.0.0" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - -colorette@^1.2.1, colorette@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -concat-with-sourcemaps@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" - integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== - dependencies: - source-map "^0.6.1" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - -convert-source-map@^1.1.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -copy-props@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.5.tgz#03cf9ae328d4ebb36f8f1d804448a6af9ee3f2d2" - integrity sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw== - dependencies: - each-props "^1.3.2" - is-plain-object "^5.0.0" - -core-js-compat@^3.14.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.0.tgz#e14a371123db9d1c5b41206d3f420643d238b8fa" - integrity sha512-8X6lWsG+s7IfOKzV93a7fRYfWRZobOfjw5V5rrq43Vh/W+V6qYxl7Akalsvgab4PFT/4L/pjQbdBUEM36NXKrw== - dependencies: - browserslist "^4.16.6" - semver "7.0.0" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cross-spawn@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" - integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI= - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= - dependencies: - array-find-index "^1.0.1" - -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0, debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -default-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" - integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== - dependencies: - kind-of "^5.0.2" - -default-resolution@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" - integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - -duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -each-props@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" - integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== - dependencies: - is-plain-object "^2.0.1" - object.defaults "^1.1.0" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -electron-to-chromium@^1.3.723: - version "1.3.752" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09" - integrity sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A== - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -error-ex@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50: - version "0.10.53" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" - integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.3" - next-tick "~1.0.0" - -es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -es6-weak-map@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - -ext@^1.1.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" - integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== - dependencies: - type "^2.0.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@^3.0.0, extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fancy-log@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - parse-node-version "^1.0.0" - time-stamp "^1.0.0" - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" - integrity sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk= - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -findup-sync@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= - dependencies: - detect-file "^1.0.0" - is-glob "^3.1.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -fined@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" - integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== - dependencies: - expand-tilde "^2.0.2" - is-plain-object "^2.0.3" - object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -flagged-respawn@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" - integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== - -flush-write-stream@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - -for-in@^1.0.1, for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -for-own@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" - integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= - dependencies: - for-in "^1.0.1" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fs-mkdirp-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" - integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= - dependencies: - graceful-fs "^4.1.11" - through2 "^2.0.3" - -fs-readdir-recursive@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" - integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -fstream@^1.0.0, fstream@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -gaze@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" - integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== - dependencies: - globule "^1.0.0" - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-stream@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" - integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= - dependencies: - extend "^3.0.0" - glob "^7.1.1" - glob-parent "^3.1.0" - is-negated-glob "^1.0.0" - ordered-read-streams "^1.0.0" - pumpify "^1.3.5" - readable-stream "^2.1.5" - remove-trailing-separator "^1.0.1" - to-absolute-glob "^2.0.0" - unique-stream "^2.0.2" - -glob-watcher@^5.0.3: - version "5.0.5" - resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.5.tgz#aa6bce648332924d9a8489be41e3e5c52d4186dc" - integrity sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw== - dependencies: - anymatch "^2.0.0" - async-done "^1.2.0" - chokidar "^2.0.0" - is-negated-glob "^1.0.0" - just-debounce "^1.0.0" - normalize-path "^3.0.0" - object.defaults "^1.1.0" - -glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globule@^1.0.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4" - integrity sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA== - dependencies: - glob "~7.1.1" - lodash "~4.17.10" - minimatch "~3.0.2" - -glogg@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" - integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== - dependencies: - sparkles "^1.0.0" - -graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -gulp-autoprefixer@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/gulp-autoprefixer/-/gulp-autoprefixer-6.1.0.tgz#5f7f78468fe99a589ce353fa5891b7bee16b8f1e" - integrity sha512-Ti/BUFe+ekhbDJfspZIMiOsOvw51KhI9EncsDfK7NaxjqRm+v4xS9v99kPxEoiDavpWqQWvG8Y6xT1mMlB3aXA== - dependencies: - autoprefixer "^9.5.1" - fancy-log "^1.3.2" - plugin-error "^1.0.1" - postcss "^7.0.2" - through2 "^3.0.1" - vinyl-sourcemaps-apply "^0.2.1" - -gulp-babel@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/gulp-babel/-/gulp-babel-8.0.0.tgz#e0da96f4f2ec4a88dd3a3030f476e38ab2126d87" - integrity sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ== - dependencies: - plugin-error "^1.0.1" - replace-ext "^1.0.0" - through2 "^2.0.0" - vinyl-sourcemaps-apply "^0.2.0" - -gulp-cli@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f" - integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== - dependencies: - ansi-colors "^1.0.1" - archy "^1.0.0" - array-sort "^1.0.0" - color-support "^1.1.3" - concat-stream "^1.6.0" - copy-props "^2.0.1" - fancy-log "^1.3.2" - gulplog "^1.0.0" - interpret "^1.4.0" - isobject "^3.0.1" - liftoff "^3.1.0" - matchdep "^2.0.0" - mute-stdout "^1.0.0" - pretty-hrtime "^1.0.0" - replace-homedir "^1.0.0" - semver-greatest-satisfied-range "^1.1.0" - v8flags "^3.2.0" - yargs "^7.1.0" - -gulp-coffee@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/gulp-coffee/-/gulp-coffee-3.0.3.tgz#a44d749e323ef9f81d9ec494b2043377a422d71a" - integrity sha512-6z5IUo5VFRavi7YWY9Z5RoKgWL72iHnwSMjIVdewFSAT90XuCkK6bkp3WyTYRe+uBanD/0gKq27/W9Q00mXphw== - dependencies: - coffeescript "^2.1.0" - plugin-error "^1.0.0" - replace-ext "^1.0.0" - through2 "^2.0.1" - vinyl-sourcemaps-apply "^0.2.1" - -gulp-concat@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" - integrity sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M= - dependencies: - concat-with-sourcemaps "^1.0.0" - through2 "^2.0.0" - vinyl "^2.0.0" - -gulp-rename@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.4.0.tgz#de1c718e7c4095ae861f7296ef4f3248648240bd" - integrity sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg== - -gulp-sass@^4.0.2: - version "4.1.0" - resolved "https://registry.yarnpkg.com/gulp-sass/-/gulp-sass-4.1.0.tgz#486d7443c32d42bf31a6b1573ebbdaa361de7427" - integrity sha512-xIiwp9nkBLcJDpmYHbEHdoWZv+j+WtYaKD6Zil/67F3nrAaZtWYN5mDwerdo7EvcdBenSAj7Xb2hx2DqURLGdA== - dependencies: - chalk "^2.3.0" - lodash "^4.17.11" - node-sass "^4.8.3" - plugin-error "^1.0.1" - replace-ext "^1.0.0" - strip-ansi "^4.0.0" - through2 "^2.0.0" - vinyl-sourcemaps-apply "^0.2.0" - -gulp-uglify@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-3.0.2.tgz#5f5b2e8337f879ca9dec971feb1b82a5a87850b0" - integrity sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg== - dependencies: - array-each "^1.0.1" - extend-shallow "^3.0.2" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - isobject "^3.0.1" - make-error-cause "^1.1.1" - safe-buffer "^5.1.2" - through2 "^2.0.0" - uglify-js "^3.0.5" - vinyl-sourcemaps-apply "^0.2.0" - -gulp@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" - integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== - dependencies: - glob-watcher "^5.0.3" - gulp-cli "^2.2.0" - undertaker "^1.2.1" - vinyl-fs "^3.0.0" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= - dependencies: - glogg "^1.0.0" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= - dependencies: - sparkles "^1.0.0" - -has-symbols@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -in-publish@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c" - integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ== - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@^1.3.4: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -interpret@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-core-module@^2.2.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" - integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== - dependencies: - has "^1.0.3" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-negated-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" - integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -is-relative@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== - dependencies: - is-unc-path "^1.0.0" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== - dependencies: - unc-path-regex "^0.1.2" - -is-utf8@^0.2.0, is-utf8@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-valid-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" - integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= - -is-windows@^1.0.1, is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -js-base64@^2.1.8: - version "2.6.4" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" - integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ== - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -just-debounce@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.1.0.tgz#2f81a3ad4121a76bc7cb45dbf704c0d76a8e5ddf" - integrity sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ== - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0, kind-of@^5.0.2: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -last-run@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" - integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= - dependencies: - default-resolution "^2.0.0" - es6-weak-map "^2.0.1" - -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= - dependencies: - readable-stream "^2.0.5" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - -lead@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" - integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= - dependencies: - flush-write-stream "^1.0.2" - -liftoff@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" - integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== - dependencies: - extend "^3.0.0" - findup-sync "^3.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= - -lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.15, lodash@~4.17.10: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -make-error-cause@^1.1.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" - integrity sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0= - dependencies: - make-error "^1.2.0" - -make-error@^1.2.0: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -make-iterator@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" - integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== - dependencies: - kind-of "^6.0.2" - -map-cache@^0.2.0, map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -matchdep@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" - integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= - dependencies: - findup-sync "^2.0.0" - micromatch "^3.0.4" - resolve "^1.4.0" - stack-trace "0.0.10" - -meow@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -mime-db@1.48.0: - version "1.48.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" - integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.31" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" - integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== - dependencies: - mime-db "1.48.0" - -minimatch@^3.0.4, minimatch@~3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.1.3, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -mute-stdout@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" - integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== - -nan@^2.12.1, nan@^2.13.2: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -next-tick@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= - -node-gyp@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" - integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== - dependencies: - fstream "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - osenv "0" - request "^2.87.0" - rimraf "2" - semver "~5.3.0" - tar "^2.0.0" - which "1" - -node-releases@^1.1.71: - version "1.1.73" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" - integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== - -node-sass@^4.8.3: - version "4.14.1" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5" - integrity sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g== - dependencies: - async-foreach "^0.1.3" - chalk "^1.1.1" - cross-spawn "^3.0.0" - gaze "^1.0.0" - get-stdin "^4.0.1" - glob "^7.0.3" - in-publish "^2.0.0" - lodash "^4.17.15" - meow "^3.7.0" - mkdirp "^0.5.1" - nan "^2.13.2" - node-gyp "^3.8.0" - npmlog "^4.0.0" - request "^2.88.0" - sass-graph "2.2.5" - stdout-stream "^1.4.0" - "true-case-path" "^1.0.2" - -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= - dependencies: - abbrev "1" - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= - -now-and-later@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" - integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== - dependencies: - once "^1.3.2" - -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.0.4, object.assign@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.defaults@^1.0.0, object.defaults@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" - integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= - dependencies: - array-each "^1.0.1" - array-slice "^1.0.0" - for-own "^1.0.0" - isobject "^3.0.0" - -object.map@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" - integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.pick@^1.2.0, object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.reduce@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" - integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -ordered-read-streams@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" - integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= - dependencies: - readable-stream "^2.0.1" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= - dependencies: - lcid "^1.0.0" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@0: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parse-filepath@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" - integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= - dependencies: - is-absolute "^1.0.0" - map-cache "^0.2.0" - path-root "^0.1.1" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -parse-node-version@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" - integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-parse@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= - dependencies: - path-root-regex "^0.1.0" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -plugin-error@^1.0.0, plugin-error@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" - integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== - dependencies: - ansi-colors "^1.0.1" - arr-diff "^4.0.0" - arr-union "^3.1.0" - extend-shallow "^3.0.2" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postcss-value-parser@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" - integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== - -postcss@^7.0.2, postcss@^7.0.32: - version "7.0.36" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb" - integrity sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -pretty-hrtime@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= - -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -psl@^1.1.28: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.5: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -"readable-stream@2 || 3": - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= - dependencies: - resolve "^1.1.6" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== - -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== - dependencies: - "@babel/runtime" "^7.8.4" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexpu-core@^4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" - integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" - -regjsgen@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== - -regjsparser@^0.6.4: - version "0.6.9" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.9.tgz#b489eef7c9a2ce43727627011429cf833a7183e6" - integrity sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ== - dependencies: - jsesc "~0.5.0" - -remove-bom-buffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" - integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== - dependencies: - is-buffer "^1.1.5" - is-utf8 "^0.2.1" - -remove-bom-stream@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" - integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= - dependencies: - remove-bom-buffer "^3.0.0" - safe-buffer "^5.1.0" - through2 "^2.0.3" - -remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - -replace-ext@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" - integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== - -replace-homedir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" - integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= - dependencies: - homedir-polyfill "^1.0.1" - is-absolute "^1.0.0" - remove-trailing-separator "^1.1.0" - -request@^2.87.0, request@^2.88.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-options@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" - integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= - dependencies: - value-or-function "^3.0.0" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.4.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -rimraf@2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sass-graph@2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.5.tgz#a981c87446b8319d96dce0671e487879bd24c2e8" - integrity sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag== - dependencies: - glob "^7.0.0" - lodash "^4.0.0" - scss-tokenizer "^0.2.3" - yargs "^13.3.2" - -scss-tokenizer@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" - integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE= - dependencies: - js-base64 "^2.1.8" - source-map "^0.4.2" - -semver-greatest-satisfied-range@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" - integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= - dependencies: - sver-compat "^1.5.0" - -"semver@2 || 3 || 4 || 5", semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= - -set-blocking@^2.0.0, set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -signal-exit@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - integrity sha1-66T12pwNyZneaAMti092FzZSA2s= - dependencies: - amdefine ">=0.0.4" - -source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -sparkles@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" - integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== - -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" - integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stack-trace@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -stdout-stream@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" - integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA== - dependencies: - readable-stream "^2.0.1" - -stream-exhaust@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" - integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -string-width@^1.0.1, string-width@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= - dependencies: - get-stdin "^4.0.1" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -sver-compat@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" - integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= - dependencies: - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -tar@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" - integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== - dependencies: - block-stream "*" - fstream "^1.0.12" - inherits "2" - -through2-filter@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" - integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== - dependencies: - through2 "~2.0.0" - xtend "~4.0.0" - -through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@~2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through2@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" - integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== - dependencies: - inherits "^2.0.4" - readable-stream "2 || 3" - -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= - -to-absolute-glob@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" - integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -to-through@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" - integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= - dependencies: - through2 "^2.0.3" - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - -"true-case-path@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d" - integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew== - dependencies: - glob "^7.1.2" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.0.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" - integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -uglify-js@^3.0.5: - version "3.13.9" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.9.tgz#4d8d21dcd497f29cfd8e9378b9df123ad025999b" - integrity sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g== - -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= - -undertaker-registry@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" - integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= - -undertaker@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.3.0.tgz#363a6e541f27954d5791d6fa3c1d321666f86d18" - integrity sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg== - dependencies: - arr-flatten "^1.0.1" - arr-map "^2.0.0" - bach "^1.0.0" - collection-map "^1.0.0" - es6-weak-map "^2.0.1" - fast-levenshtein "^1.0.0" - last-run "^1.1.0" - object.defaults "^1.0.0" - object.reduce "^1.0.0" - undertaker-registry "^1.0.0" - -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== - -unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -unique-stream@^2.0.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" - integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== - dependencies: - json-stable-stringify-without-jsonify "^1.0.1" - through2-filter "^3.0.0" - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -v8flags@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== - dependencies: - homedir-polyfill "^1.0.1" - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -value-or-function@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" - integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vinyl-fs@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" - integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== - dependencies: - fs-mkdirp-stream "^1.0.0" - glob-stream "^6.1.0" - graceful-fs "^4.0.0" - is-valid-glob "^1.0.0" - lazystream "^1.0.0" - lead "^1.0.0" - object.assign "^4.0.4" - pumpify "^1.3.5" - readable-stream "^2.3.3" - remove-bom-buffer "^3.0.0" - remove-bom-stream "^1.2.0" - resolve-options "^1.1.0" - through2 "^2.0.0" - to-through "^2.0.0" - value-or-function "^3.0.0" - vinyl "^2.0.0" - vinyl-sourcemap "^1.1.0" - -vinyl-sourcemap@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" - integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= - dependencies: - append-buffer "^1.0.2" - convert-source-map "^1.5.0" - graceful-fs "^4.1.6" - normalize-path "^2.1.1" - now-and-later "^2.0.0" - remove-bom-buffer "^3.0.0" - vinyl "^2.0.0" - -vinyl-sourcemaps-apply@^0.2.0, vinyl-sourcemaps-apply@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" - integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU= - dependencies: - source-map "^0.5.1" - -vinyl@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" - integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@1, which@^1.2.14, which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -xtend@~4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^3.2.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" - integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== - -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yargs-parser@^13.1.2: - version "13.1.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.1.tgz#7ede329c1d8cdbbe209bd25cdb990e9b1ebbb394" - integrity sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA== - dependencies: - camelcase "^3.0.0" - object.assign "^4.1.0" - -yargs@^13.3.2: - version "13.3.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" - integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.2" - -yargs@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.2.tgz#63a0a5d42143879fdbb30370741374e0641d55db" - integrity sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA== - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^5.0.1"